2022/12/15

golang option pattern

 - golang option pattern

- option struct
```
type option struct {
Name string
Age int
}
```
- option func type
```
type Option func(*options)

func F1(name string) Option{
return fun(op *option){
op.Name = name
}
}
}

func F2(age int) Option{
return func(op *option) {
op.Age= age
}
}
```
- usage
```
func NewOption(setters ...Option){
op := &option{}
for s:=range setters{
s(op)
}
return op
}

NewOption(F1("tom"),F2(18))
```

2022/12/14

what is good bulk insert

To get optimal write throughput for bulk loads, partition your data by primary key with this pattern:
  Each partition contains a range of consecutive rows. Each commit contains data for only a single partition. A good rule of thumb for your number of partitions is 10 times the number of nodes in your Cloud Spanner instance. So if you have N nodes, with a total of 10*N partitions, you can assign rows to partitions by:

  Sorting your data by primary key. Dividing it into 10*N separate sections. Creating a set of worker tasks that upload the data. Each worker will write to a single partition. Within the partition, it is recommended that your worker write the rows sequentially. However, writing data randomly within a partition should also provide reasonably high throughput.

  As more of your data is uploaded, Cloud Spanner automatically splits and rebalances your data to balance load on the nodes in your instance. During this process, you may experience temporary drops in throughput.

  Following this pattern, you should see a maximum overall bulk write throughput of 10-20 MiB per second per node.

golang chunk

 - chunk

```
func Chunk[T any](items []T, chunkSize int) [][]T {
var chunks = make([][]T, 0, (len(items)/chunkSize)+1)
for chunkSize < len(items) {
items, chunks = items[chunkSize:], append(chunks, items[0:chunkSize:chunkSize])
}
return append(chunks, items)
}
//Arrayをスライシングすると、リターンされたSliceのcapはstartIndexからArrayの最期までのサイズです。
//しかし、次のようにスライシングする時、maxIndexを追加してcapのサイズを調整することが出来ます。
//slice[startIndex:endIndex:maxIndex]
```

big array goroutine

 - https://qiita.com/tutuz/items/057452fbbe9a5ae26d37

- multi goroutine to loop array
```
slice := []string{"a", "b", "c", "d", "e"}
sliceLength := len(slice)
var wg sync.WaitGroup
wg.Add(sliceLength)
fmt.Println("Running for loop…")
for i := 0; i < sliceLength; i++ {
go func(i int) {
defer wg.Done()
val := slice[i]
fmt.Printf("i: %v, val: %v\n", i, val)
}(i)
}
wg.Wait()
fmt.Println("Finished for loop")
```
- 課題はエラー検知とgoroutineの数の制御、errgroupの登場
- golang.org/x/sync/errgroup は、複数の goroutine を実行して、それらのうちにエラーがあったときにエラーを知る、ということを可能にしてくれるライブラリ
- sync.WaitGroup は実行した goroutine が終わるのを待ちますが、エラーがあったかどうかはわかりません
- errgroup は sync.WaitGroup+error といったイメージで、どれかの goroutine でエラーがあったら最初のひとつを知ることができ
```
// You can edit this code!
// Click here and start typing.
package main

import (
"context"
"fmt"

"golang.org/x/sync/errgroup"
"golang.org/x/sync/semaphore"
)

func main() {
fmt.Println("Hello, 世界")
ctx := context.TODO()
ms := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
mcs := make([]int, len(ms))

eg := errgroup.Group{}
sem := semaphore.NewWeighted(3)
for i := range ms {
if err := sem.Acquire(ctx, 1); err != nil {
panic(err)
}
func(i int) {
eg.Go(
func() error {
defer sem.Release(1)
fmt.Printf("i is %v\n", i)
mcs[i] = ms[i] + 100
return nil
})
}(i)
}

if err := eg.Wait(); err != nil {
panic(err)
}
fmt.Printf("ms is %v\n", ms)
fmt.Printf("msc is %v\n", mcs)
}
```

2022/12/08

golang fmt %q

文字列を””で囲んで出力する。
これで実際の を””に表現して、わかりやすい 

spannerのgolang clientで%qを使っている
It's just quite a strange decision made by Google here, to assume that the String() function is only used for logging and it's ok to add quotes...

package main


import (

"fmt"

)


func main() {

str := "Gopherくん"

slice := []string{"g", "o", "h", "e", "r"}

integer := 12450

fmt.Printf("1:出力は %q となります。\n", str)

fmt.Printf("2:出力は %s となります。\n", str)


fmt.Printf("3:出力は %q となります。\n", slice)

fmt.Printf("4:出力は %s となります。\n", slice)


fmt.Printf("5:出力は %d となります。\n", integer)

fmt.Printf("6:出力は %q となります。\n", integer)

}


//1:出力は "Gopherくん" となります。

//2:出力は Gopherくん となります。

//3:出力は ["g" "o" "h" "e" "r"] となります。

//4:出力は [g o h e r] となります。

//5:出力は 12450 となります。

//6:出力は 'ア' となります。 (アのunicode の十進制は12345)


2022/12/07

spanner nullable data type golang

 spanner nullable data type

```
// NullableValue is the interface implemented by all null value wrapper types.
type NullableValue interface {
// IsNull returns true if the underlying database value is null.
IsNull() bool
}
```
- how to use
- difference between null,"","someValue"
- null
- spanner.NullString(valid=false)
- ""
- spanner.NullString(valid=true,value="")
- update(value[*NullableValue]->model[NullableValue])
- update target items
- value is not nil
- not update target items
- value is nil
- vo is all pointer

by the way, another orm tool,gorm , don't solve this problem well.
var user User
db.First(&user)

db.Model(&user).Updates(User{Age: 18, Name: nil})
// 実行 SQL : UPDATE `users` SET `age` = '18'  WHERE `users`.`id` = '1'

GORM のドキュメントを見ると以下のように書いてあったので、構造体で実行すると無視されてしまうようです。 When query with struct, GORM will only query with those fields has non-zero value,

that means if your field's value is 0, '', false or other zero values,

it won't be used to build query conditions,

go instal where ?

go install xxx..xxx

==>the location is ~/go/bin
so ,set the ~/go/bin to .bash_profiles 's PATH


go version

go help environment

```

GOBIN

The directory where 'go install' will install a command.

GOPATH

For more details see: 'go help gopath'.

GOROOT

The root of the go tree.

```

go help gopath

```

If the environment variable is unset, GOPATH defaults

to a subdirectory named "go" in the user's home directory

($HOME/go on Unix, %USERPROFILE%\go on Windows),

unless that directory holds a Go distribution.

Run "go env GOPATH" to see the current GOPATH.

```

go env GOPATH

==>~/go

go help install

````

usage: go install [build flags] [packages]


Install compiles and installs the packages named by the import paths.


Executables are installed in the directory named by the GOBIN environment

variable, which defaults to $GOPATH/bin or $HOME/go/bin if the GOPATH

environment variable is not set. Executables in $GOROOT

are installed in $GOROOT/bin or $GOTOOLDIR instead of $GOBIN.

```

golang model

https://go.dev/play/p/B7ErJjt8JqU 

modelの役割ですが、
① entity,usecase,repo,querierなど繋ぐ
② validationなどの業務ロジックの担保
②の配慮でmodelのfiledは非公開で、Setter都度Validationする、常に完璧な状態を求めて、
trade-offで使いにくく、①の役割を損する。
なので、①の役割をPrimativeなstruct(一旦voと呼ぶ)に分担する方いいかなと思う。
vo<=>modelの変換は手間ですが、複雑ではないので、ある程度自動作成できる。

-- go.mod --

module sample

go 1.19
-- model/name.go --
package model

import "errors"

type Name struct {
string
}

func NewName(v string) (Name, error) {
n := Name{v}
if err := n.validate(); err != nil {
return Name{}, err
}

return n, nil
}

func (n Name) validate() error {
if len(n.string) > 10 {
return errors.New("name length must be less than equal 10")
}
return nil
}

func (n Name) String() string {
return n.string
}
-- model/user.go --
package model

import "errors"

// entity
type User struct {
id string
name Name
age *int
}

func (u User) validate() error {
if u.id == "" {
return errors.New("id must not be empty")
}
if err := u.name.validate(); err != nil {
return err
}
return nil
}

func NewUser(vo UserVO) (User, error) {
return vo.ToModel()
}

func InitUser(vo UserVO) (User, error) {
vo.ID = "1234567890" // random
return vo.ToModel()
}

func (u User) ToVO() UserVO {
return UserVO{
ID: u.id,
Name: u.name.String(),
Age: u.age,
}
}

type UserVO struct {
ID string
Name string
Age *int
}

func (uvo UserVO) ToModel() (User, error) {
u := User{
id: uvo.ID,
name: Name{uvo.Name},
age: uvo.Age,
}
if err := u.validate(); err != nil {
return User{}, err
}
return u, nil
}
-- main.go --
package main

import (
"fmt"
"sample/model"
)

func main() {
fmt.Println("success pattern")
age := 12
//New
vo := model.UserVO{
ID: "s11600",
Name: "kume",
Age: &age}
u, err := model.NewUser(vo)
if err != nil {
fmt.Println("err:", err)
}
fmt.Printf("valid user, %+v\n", u)

//Init
vo = model.UserVO{
Name: "ku",
Age: &age}
u, err = model.InitUser(vo)
if err != nil {
fmt.Println("err:", err)
}
fmt.Printf("valid user, %+v\n", u)

//update
vo = u.ToVO()
vo.Name = "updated"
vo.Age = nil
u, err = vo.ToModel()
if err != nil {
fmt.Println("err:", err)
}
fmt.Printf("update success, %+v\n", u)



fmt.Println("--------------")
fmt.Println("failed pattern")
vo.Name = "invalid name"
u, err = vo.ToModel()
if err != nil {
fmt.Println("err:", err)
}
fmt.Printf("update failed, %+v\n", u)
}


2022/12/02