Skip to content

Instantly share code, notes, and snippets.

@terut
Last active November 14, 2022 07:25
Show Gist options
  • Save terut/2c265388cfc3735bfe11494d3c11834b to your computer and use it in GitHub Desktop.
Save terut/2c265388cfc3735bfe11494d3c11834b to your computer and use it in GitHub Desktop.
version: '3'
services:
# Spanner
spanner-emulator:
image: gcr.io/cloud-spanner-emulator/emulator
ports:
- "9010:9010"
- "9020:9020"
# Init (Create Instance)
spanner-emulator-init:
image: gcr.io/google.com/cloudsdktool/cloud-sdk:slim
command: >
bash -c 'gcloud config configurations create emulator;
gcloud config set auth/disable_credentials true;
gcloud config set project $${PROJECT_ID};
gcloud config set api_endpoint_overrides/spanner $${SPANNER_EMULATOR_URL};
gcloud spanner instances create $${INSTANCE_NAME} --config=emulator-config --description=Emulator --nodes=1;
gcloud spanner databases create $${DATABASE_NAME} --instance=$${INSTANCE_NAME}'
# linux only
#extra_hosts:
# - "host.docker.internal:host-gateway"
environment:
PROJECT_ID: "test-project"
SPANNER_EMULATOR_URL: "http://spanner-emulator:9020/"
INSTANCE_NAME: "test-instance"
DATABASE_NAME: "test-database"
package fixture
import (
"context"
"fmt"
"reflect"
"cloud.google.com/go/spanner"
)
type Fixture struct {
Cli *spanner.Client
}
func (f *Fixture) Setup(ctx context.Context, seed []Seed) (func() error, error) {
msIns, msDel, err := CreateMutations(seed)
if err != nil {
return nil, err
}
if _, err := f.Cli.Apply(ctx, msIns); err != nil {
return nil, err
}
return func() error {
_, err := f.Cli.Apply(ctx, msDel)
return err
}, nil
}
func CreateMutations(seed []Seed) ([]*spanner.Mutation, []*spanner.Mutation, error) {
var msIns []*spanner.Mutation
var msDel []*spanner.Mutation
for _, ss := range seed {
keys := make([]spanner.Key, 0, len(ss.List))
for _, s := range ss.List {
mi, err := spanner.InsertStruct(ss.Table, s)
if err != nil {
return nil, nil, err
}
msIns = append(msIns, mi)
k, err := key(reflect.ValueOf(s))
if err != nil {
return nil, nil, err
}
keys = append(keys, k)
}
msDel = append(msDel, spanner.Delete(ss.Table, spanner.KeySetFromKeys(keys...)))
}
for i := len(msDel)/2 - 1; i >= 0; i-- {
opp := len(msDel) - 1 - i
msDel[i], msDel[opp] = msDel[opp], msDel[i]
}
return msIns, msDel, nil
}
func key(v reflect.Value) (spanner.Key, error) {
rt := v.Type()
if rt.Kind() == reflect.Ptr {
return key(v.Elem())
}
if rt.Kind() != reflect.Struct {
return nil, err(fmt.Errorf("invalid seed type %v, use struct", rt.Kind()))
}
var key spanner.Key
for i := 0; i < rt.NumField(); i++ {
f := rt.Field(i)
if f.Tag.Get("fixture") != "key" {
continue
}
key = append(key, v.FieldByName(f.Name).Interface())
}
return key, nil
}
type Seed struct {
Table string
List []any
}
func err(msg any) error {
return fmt.Errorf("fixture/fixture: %v", msg)
}
type Seed interface {
S()
}
func NewSeed(ss ...interface{}) [][]Seed {
seeds := make([][]Seed, 0, len(ss))
for _, ssi := range ss {
ssVal := reflect.ValueOf(ssi)
switch ssVal.Kind() {
case reflect.Slice:
seed := make([]Seed, 0, ssVal.Len())
for i := 0; i < ssVal.Len(); i++ {
si := ssVal.Index(i).Interface()
if s, ok := si.(Seed); ok {
seed = append(seed, s)
}
}
seeds = append(seeds, seed)
case reflect.Struct, reflect.Pointer:
si := ssVal.Interface()
if s, ok := si.(Seed); ok {
seeds = append(seeds, []Seed{s})
}
}
}
return seeds
}
type result struct {
server string
err error
}
var (
ErrConfig = errors.New("config get error")
)
func conn() error {
servers := []string{"127.0.0.1:11211", "127.0.0.1:11211", "127.0.0.1:11211"}
resultChan := make(chan result, len(servers))
wg := new(sync.WaitGroup)
for _, server := range servers {
wg.Add(1)
go func(server string) {
defer wg.Done()
nc, err := net.Dial("tcp", server)
if err != nil {
resultChan <- result{server: server, err: err}
}
defer nc.Close()
_, err = nc.Write([]byte("get key1\r\n"))
if err != nil {
resultChan <- result{server: server, err: err}
}
buf := make([]byte, 4096)
n, err := nc.Read(buf)
if err != nil {
resultChan <- result{server: server, err: err}
}
if strings.HasSuffix(string(buf[:n]), "ERROR\r\n") {
resultChan <- result{server: server, err: ErrConfig}
}
if string(buf[:n]) == "END\r\n" {
fmt.Println("cache miss")
}
fmt.Println(string(buf[:n]))
resultChan <- result{server: server, err: nil}
}(server)
}
go func() {
wg.Wait()
close(resultChan)
}()
for result := range resultChan {
if result.err != nil && errors.Is(result.err, ErrConfig) {
fmt.Printf("%v, server=%v\n", result.err, result.server)
}
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment