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

2022/11/25

2022/11/22

sql join on using


select * from join1 inner join join2 on join1.join1_id = join2.join1_id;

select * from join1 inner join join2 using(join1_id);


usingで同じ名の列は一つになり、列減少する
基本、usingの方が便利と思う。

big query 配列 uunest

 キー以外の内容はkey-valueの配列かな
RDBと違う
配列の中身を取るに UNNEST関数を使う
NESTは階層で、UNNESTは階層をなくす、平準化にする
例えば。
epは配列で
select 他のカラム、ep.key, ep.value.string_value
from xxxx, UNNEST(配列のカラム) ep

以上は検索できるが、
問題は、配列中の値の複数を条件として検索できない
ep.a=a AND ep.b=b で取れない
で、それで別々に検索して、JIONなどのSQL技で解決。
複雑なSQLになる。


SELECT GENERATE_ARRAU(11,33,2) AS some
start,end,step(default 1)

+--------------------------------------------------+
| some                                             |
+--------------------------------------------------+
| [11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33] |
+--------------------------------------------------+


GENERATE_ARRAU(1,10)


cloud spanners shard

 - record->split->node
 - どこに保存されるはprimary keyで決める
- autoIncrementの仕組みがない、順番に保存するのは特定のnodeに集中するので、望ましくない(hotspot発生)
- autoIncrement以外のIDの生成方法

uuid 1~uuid4

  - uuid1はtimestampペース、mac addressを使う
連番になる可能性
- uuid4はランダムの乱数で、分散性が高い、衝突になる可能性が0ではない

snowflake

ULID

timestampを使ってIDを生成する場合、どうしても分散率が低い。

なので、
shardId=hash(key)%N
PRIMARY KEY(shardId,分散率低い値のcolumn)
の複合キーで対応する

内部のHash関数を使う。
 - farm_fingerprint
 - など




2022/07/04

@MockkBean SpykBean

 @MockkBeanではevery文で定義しなかった関数は何も返さない関数に置き換わりますが、

@SpykBeanではevery文で定義しなかった関数はそのままオリジナルの関数の定義を引き継ぎます。

2022/06/29

What is username and password when starting Spring Boot with Tomcat?

user name: user

password: 

2022-06-29 17:17:09.586  WARN 1242 --- [           main] .s.s.UserDetailsServiceAutoConfiguration :


Using generated security password: 7b324ed9-1181-461e-bd5e-0a26ebdd3013


This generated password is for development use only. Your security configuration must be updated before running your application in production.




curl post json file

 curl -X POST -H 'Content-type:application/json' \

  --data-binary @some.json some_url

2022/02/28

2022/02/20

mac java version

久しぶりにJavaを触った。

many many jdks ...so how to mange so many jdks


  • show jdks version on your mac pc
    /usr/libexec/java_home -V

  • env is a good idea. 
    • python->pyenv
    • node->nodenv
    • ruby->rbenv
    • golang->gvm
    • java->jenv

  • install jenv
      brew install jenv
     export JENV_ROOT="$HOME/.jenv"
    echo 'eval "$(jenv init -)"' >> ~/.bash_profile
    source ~/.bash_profile
    jenv doctor

  • add jdk path to jenv
    jenv add /Library/Java/JavaVirtualMachines/jdk-12.0.2.jdk/Contents/Home

  • use some version
    jenv local 11     

2022/02/16

Gradle The JavaExec.main property has been deprecated

deprecated gradle features were used in this build, making it incompatible with gradle 8.0.


 => add ↓ to gradle.properties

org.gradle.warning.mode=all


then 

he JavaExec.main property has been deprecated


change build.gradle


plugins {
id "application"
}
apply plugin : "java"
application {
mainClass = 'xxx.xxx'
}

then 

gradle run