Postgres pg_rewind gotcha
I use pg_rewind for rewind a Postgres cluster which is ahead of the primary. Yesterday, after the rewind Postgres didn’t start. It failed with the below error. This is using 9.6 version pg_rewind client.
Sep 2 20:06:14.371503 productiondb[1]: [1-1] time=2022-09-02 20:06:14.338 GMT,pid=78197,user=admin,db=postgres,client=[local],appname=[unknown],vid=,xid=0 FATAL: the database system is starting up
Sep 2 20:06:14.371611 productiondb[1]: [1-1] time=2022-09-02 20:06:14.370 GMT,pid=78184,user=,db=,client=,appname=,vid=,xid=0 LOG: entering standby mode
Sep 2 20:06:14.371991 productiondb[1]: [1-1] time=2022-09-02 20:06:14.371 GMT,pid=78184,user=,db=,client=,appname=,vid=,xid=0 PANIC: could not open file "pg_replslot/ae0750b1/state": No such file or directory
Sep 2 20:06:14.371991 productiondb[1]: [1-1] time=2022-09-02 20:06:14.371 GMT,pid=78182,user=,db=,client=,appname=,vid=,xid=0 LOG: startup process (PID 78184) was terminated by signal 6: Aborted
Of course, the /state file didn’t exist inside the replication slot sub-directory ae0750b1.
functional options
I saw this pattern of a function taking optional parameters (variadic arguments) and I never understood why they do that until today.
There is a bit of a background on why I need to use functional pattern.
Let’s say I don’t use functional option. Then, all the arguments need to be passed. Also, difficult to set default values and implement logic for default values.
package main
import (
"fmt"
"log"
)
type Config struct {
Host string
Port uint
Visibility bool
}
type clientOption func(c *Config) error
func newConfig(host string, opts ...clientOption) Config {
c := Config{
Host: host,
}
for _, option := range opts {
err := option(&c)
if err != nil {
log.Printf("error %v", err)
}
}
return c
}
func withPort(port uint) clientOption {
return func(c *Config) error {
if port > 65000 {
return fmt.Errorf("port can't be higher than 65000")
}
c.Port = port
if c.Port == 0 {
c.Port = 5432
}
return nil
}
}
func main() {
c := newConfig("localhost", withPort(100000))
fmt.Println("config: ", c)
}
Understanding pointers in Go
- double pointers
A pointer (whose value is another variable’s address) which points to another pointer
-
copying a pointer to another pointer copies the address
-
copying a de-referenced pointer to another de-referenced pointer updates the value of the one to another
https://gist.github.com/viggy28/604cda186ff1b872d562b31ca3976732
package main
import "fmt"
type DB struct {
Name string
}
// double pointer
// a pointer which points to another pointer
func wrapperFunc(config **DB) func() error {
return func() error {
cfg := &DB{}
(*cfg).Name = "hello"
*config = cfg
return nil
}
}
// config = cfg assigns the address which cfg has to config
// hence dd1 in main() doesn't get impacted
func wrapperFunc1(config *DB) func() error {
return func() error {
cfg := &DB{}
fmt.Println("address of config: ", &config)
fmt.Printf(" wrapperFunc1 A: config is %p, cfg is %p\n", config, cfg)
cfg.Name = "hello"
config = cfg
fmt.Printf(" wrapperFunc1 B: config is %p, cfg is %p\n", config, cfg)
return nil
}
}
// *config = *cfg value at address cfg has will be copied to value at the address that config has
// hence dd2 in main() does gets updated
func wrapperFunc2(config *DB) func() error {
return func() error {
cfg := &DB{}
fmt.Printf(" wrapperFunc2 A: config is %p, cfg is %p\n", config, cfg)
cfg.Name = "hello"
fmt.Println("value at ", *cfg)
*config = *cfg
fmt.Printf(" wrapperFunc2 B: config is %p, cfg is %p\n", config, cfg)
return nil
}
}
func wrapperFunc3(config *DB) func() error {
return func() error {
config.Name = "hello"
return nil
}
}
func wrapperFunc4(config DB) func() error {
return func() error {
config.Name = "hello"
return nil
}
}
func main() {
{
var d *DB
fn := wrapperFunc(&d)
fn()
fmt.Printf("d.Name: %q\n", d.Name)
}
{
dd1 := &DB{}
fmt.Println("address of dd1: ", &dd1)
fmt.Printf("main A: dd1 is %p\n", dd1)
fn1 := wrapperFunc1(dd1)
fn1()
fmt.Printf("main B: dd1 is %p\n", dd1)
fmt.Printf("dd1.Name: %q\n", dd1.Name)
}
{
dd2 := &DB{}
fmt.Printf("main C: dd2 is %p\n", dd2)
fn2 := wrapperFunc2(dd2)
fn2()
fmt.Printf("main D: dd2 is %p\n", dd2)
fmt.Printf("dd2.Name: %q\n", dd2.Name)
}
{
dd3 := &DB{}
fn3 := wrapperFunc3(dd3)
fn3()
fmt.Printf("dd3.Name: %q\n", dd3.Name)
}
{
dd4 := DB{}
fn4 := wrapperFunc4(dd4)
fn4()
fmt.Printf("dd4.Name: %q\n", dd4.Name)
}
}

Misc notes on Go
-
go run command
go runcommand is a quick way to run go program. However, what it does behind the screen is it compiles the program and runs an executable and removes it once the program is stopped. -
byte is a built-in alias of uint8. rune is a built-in alias of int32.
-
The semicolon insertion rule In Go, I thought there is no need for semicolon at the end of the statement. It is true (as a developer I don’t need to) however lexer (which I don’t know what it is; sounds similar to compiler, interpretor) does that behind the screen. Refer https://golang.org/doc/effective_go#semicolons
encoding miscellaneous
From the wiki,
In computing, data storage, and data transmission, character encoding is used to represent a repertoire of characters by some kind of encoding system that assigns a number to each character for digital representation.
Okay, they mean converting character to some number for storing and transmitting data.
There are two popular character sets
- ASCII
- American Standard Code for Information Interchange
- Pretty much all the characters and symbols in modern keyboard comes under ASCII
- Total of 128 characters
- Decimal value between 65 and 90 represents captial alphabets and 97 to 122 represents small alphabets
- UTF-8
- ASCII doesn’t cover characters from other languages.
- UTF-8 is capable of encoding all 1,112,064 valid character code points in Unicode using one to four one-byte (8-bit) code units.
- It has backward compatible with ASCII
I run into character set in postgres. There is client_encoding and server_encoding setting.
Notes on makefile
There are two types of variable declaration.
- recursively expanded variables -
foois assigned withbarwhich inturns assigned toughwhose value isHuh?
foo = $(bar)
bar = $(ugh)
ugh = Huh?
all:;echo $(foo)
$ make
$ Huh?
- simply expanded variables -
foois assigned withbarhowever bar is not defined yet. so it outputs to nothing.
foo := $(bar)
bar := $(ugh)
ugh := Huh?
all:;echo $(foo)
$ make
$
NOTE: To echo something it needs to be in a target. Here the target name is all.