Liveness vs Readiness Health Checks in Application

The liveness check finds if the application is alive or not. The application defines the logic and the liveness check verifies the expected output.
A webserver liveness check could include an HTTP handler that returns a fixed response or checks application dependencies.

Sample Liveness check

  • send a pong response in the handler
  • combine health check of all dependencies and return true/false

The Readiness check is to know if an application can accept traffic after initialization. A use case is a wait to serve traffic until all dependencies are ready. Essentially both checks have similar config options.

Sample Readiness Check

  • combine health check of all dependencies and return true/false
  • A init wait of 5 secs

References

Written with StackEdit.

Why be careful with defer in Golang

When does defer function executes?

The standard answer is: defer runs a function before the enclosing function returns.

It is a wrong explanation. The defer executes as follows:

  1. The enclosing function has processed returned values and stored them in registers
  2. All defer functions run in a LIFO order of definition.
  3. If the return parameters are named, the defer function can alter the return values.

Short Examples on Vulnerabilities in defer Code

defer Modifying Named Return Parameters

package main

import (
	"fmt"
	"sync"
)

func test4(x int) (y int) {
	defer func() {
		y = 100
	}()

	if x == 123 {
		return x
	}

	defer func() {
		y = 200
	}()

	return x
}

func main() {
	fmt.Printf("Hello x=%v\n", test4(1234))
}

The above code returns value 123 referenced by y. Now, defer functions start executing. Mind it, the function test4() is yet to return to main().

  • The last defer function modifies the return value y to 200.
  • The first defer function modifies the return value y to 100.
  • test4() returns 100.

Deadlocked Program with Improper Understanding of defer

func test5(abort bool) {
	var mutex = &sync.Mutex{}

	defer func() {
		fmt.Printf("locking the mutex-first\n")
		mutex.Unlock()
	}()

	if abort {
		mutex.Lock()
		fmt.Println("critical section")
		mutex.Unlock()
		return
	}

	defer func() {
		fmt.Printf("locking the mutex-second\n")
		mutex.Lock()
	}()

}

func main() {
	test5(true)
}
go run deferDetails/main.go
critical section
locking the mutex-first
fatal error: sync: unlock of unlocked mutex

goroutine 1 [running]:
runtime.throw(0x10d2d55, 0x1e)
        /Users/ovo/.goenv/versions/1.14.0/src/runtime/panic.go:1112 +0x72 fp=0xc000078e38 sp=0xc000078e08 pc=0x102e542
sync.throw(0x10d2d55, 0x1e)
        /Users/ovo/.goenv/versions/1.14.0/src/runtime/panic.go:1098 +0x35 fp=0xc000078e58 sp=0xc000078e38 pc=0x102e4c5
sync.(*Mutex).unlockSlow(0xc00018c008, 0xc0ffffffff)
        /Users/ovo/.goenv/versions/1.14.0/src/sync/mutex.go:196 +0xd6 fp=0xc000078e80 sp=0xc000078e58 pc=0x106a746
sync.(*Mutex).Unlock(...)
        /Users/ovo/.goenv/versions/1.14.0/src/sync/mutex.go:190
main.test5.func1(0xc00018c008)
        /Users/ovo/go/1.14.0/src/mygo/deferDetails/main.go:13 +0x8b fp=0xc000078ee0 sp=0xc000078e80 pc=0x109ec7b
main.test5(0xc00006c001)
        /Users/ovo/go/1.14.0/src/mygo/deferDetails/main.go:20 +0x11a fp=0xc000078f70 sp=0xc000078ee0 pc=0x109eb1a
main.main()
        /Users/ovo/go/1.14.0/src/mygo/deferDetails/main.go:31 +0x26 fp=0xc000078f88 sp=0xc000078f70 pc=0x109ebd6
runtime.main()
Explanation

ha! the defer caused a panic in the program. I am not quite sure why it failed. If I remove the return, the code works.

package main

import (
	"fmt"
	"sync"
)

func test5(abort bool) {
	var mutex = &sync.Mutex{}

	defer func() {
		fmt.Printf("locking the mutex-first\n")
		mutex.Unlock()
	}()

	if abort {
		mutex.Lock()
		fmt.Println("critical section")
		mutex.Unlock()
	}

	defer func() {
		fmt.Printf("locking the mutex-second\n")
		mutex.Lock()
	}()

}

func main() {
	test5(true)
}
go run deferDetails/main.go
critical section
locking the mutex-second
locking the mutex-first

Conclusion

  1. Use defer very carefully, especially around named return parameter
  2. Multiple defer statements are executed in reverse order of appearance.
  3. Need to root cause why the code with return panic.

References

Written with StackEdit.

Time Complexity of a Memoized Algorithm

The memoization eliminated duplicate calls to a function. A memoized implementation of Fibonacci code is as follows:

package complexity

import "fmt"

var mem map[int]int

func fibo(count int) int {
	if count == 0 || count == 1 {
		return count
	}

	if mem[count] != -1 {
		return mem[count]
	}

	s := fibo(count-1) + fibo(count-2)
	mem[count] = s
	return s
}

func Fibo(count int) {
	mem = make(map[int]int, count)

	for i := 0; i <= count; i++ {
		mem[i] = -1
	}

	fmt.Printf("fibonacci number for count=%+v is %v\n", count, fibo(count))
	fmt.Printf("fibonacci map is %v\n", mem)
}
$ go run main.go
fibonacci number for count=4 is 3
fibonacci map is map[0:-1 1:-1 2:1 3:2 4:3]

Since each function in the recursion tree is processed at most once, the worst-case complexity of the above code becomes O(N).

Reference
https://stackoverflow.com/questions/42617249/time-complexity-of-memoization-fibonacci

Written with StackEdit.

Set and Clear Bits in Golang

Set and Clear Bits in Golang

There are bitwise operators in golang for bit manipulation. However, there is an unusual NOT operator. golang uses the ^ operator for NOT.

Code

NOT of a number

a := 101
notOfA := a ^ a // 101 ^ 101 = 010 

Setting a bit of a number

a := 101
b := 1 << 2 // second second bit
setBit := a | b // 101 | 010 = 111 

Clear a bit

a := 101
b := 1 << 2 // second second bit

// 101 & ^(010) => 101 & 101 => 101
setBit := a &^ b  

References

Written with StackEdit

git pull, merge and rebase

It is always better to rebase local commits while pulling remote published changes. git merge will create a merge entry in the history, rebase is preferred because it orders your local commit on top of the latest remote commits.

To avoid typing --rebase whenever you pull you can config git to use it as default:

git config --global pull.rebase true

The equivalent .gitconfig is:

# This is Git's per-user configuration file.
[pull]
	rebase = true

References

Written with StackEdit.

Summary: How to Write Go Code

Summary: How to Write Go Code

  1. The code organization follows repository -> module -> packages.
  2. Use go mod init and initialize module root as desired e.g. example.com/user/hello
  3. The Go binary goes to the path set in GOBIN
  4. All imported modules are stored in GOPATH/pkg/mod subdirectory
  5. The command go clean -modcache cleans downloaded packages.

Reference

Written with StackEdit.

MySQL: Convert Character Set and Collation

The character set and collation changes for a DB have important changes on the storage requirements and query result behavior. The character set for a table can be changed in two ways:
a. Using CONVERT TO query
b. Using MODIFY <column name>

Methods

The CONVERT TO method makes sure that each column fits the new character set range after conversion. So a column type TEXT of character set Latin would not accommodate the character set of UTF8MB4. Since the latter needs 4 bytes for a character, while in Latin one byte is required for a character.

So CONVERT TO rounds of the column type to the nearest next size.

The next method of MODIFY does not make any change to the column type.

Conclusion

I prefer the first method because it’s assuring that the table column sizes are technically large enough and also it eliminates any manual changes for a column.

Examples

ALTER DATABASE <db name> CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;

ALTER TABLE `test_table` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
ALTER TABLE `test_table` MODIFY `column1` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL;

ALTER TABLE `test_table` MODIFY `column2` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci NOT NULL;

References

How to: MySQL Get Database Name and Use in Another Query

How to: MySQL Get Database Name and Use in Another Query

The following SQL query get the database name and use the name to run another query.

SET @dbname = DATABASE();

ALTER DATABASE @dbname CHARACTER SET utf8 COLLATE utf8_unicode_ci; 

References

%d bloggers like this: