Open5

DynamoDB小技 (guregu/dynamo)

gureguguregu

アトミックカウンター

Autoincrementみたいなサムシング

func NextID(ctx context.Context, class string) (n int, err error) {
	var counter struct {
		Class string
		Count int
	}

	table := dynamoTable("Counters")
	err = table.Update("Class", class).Add("Count", 1).ValueWithContext(ctx, &counter)
	return counter.Count, err
}

使い方


type User struct {
	ID       int
	Regdate  time.Time
	// ...
}

func (u *User) Create(ctx context.Context) error {
	if u.ID != 0 {
		panic("user already exists")
	}
	now := time.Now().UTC()
	u.Regdate = now

	var err error
	u.ID, err = NextID(ctx, counterUsers)
	if err != nil {
		return err
	}

	users := dynamoTable(tableUsers)
	err = users.Put(u).If("attribute_not_exists(ID)").RunWithContext(ctx)
	return err
}
gureguguregu

errorがConditionalCheckFailedExceptionか否か

func IsCondCheckErr(err error) bool {
	if ae, ok := err.(awserr.Error); ok && ae.Code() == "ConditionalCheckFailedException" {
		return true
	}
	return false
}
gureguguregu

テーブルにprefix付けると管理しやすい

const tablePrefix = "Hoge-"

var db = dynamo.New(session.New(), &aws.Config{
	Region: aws.String("us-west-2"),
	// LogLevel: aws.LogLevel(aws.LogDebugWithHTTPBody),
})

func dynamoTable(name string) dynamo.Table {
	return db.Table(tablePrefix + name)
}
gureguguregu

こうすれば一々importせずにエラー判定できる

package hoge
import "github.com/guregu/dynamo"

var ErrNotFound = dynamo.ErrNotFound
gureguguregu

Listな属性が存在しなければ初期化、存在していたらappend

update.SetExpr("'Usage' = list_append(if_not_exists('Usage', ?), ?)", []InviteUsage{}, []InviteUsage{{Time: time.Now().UTC(), UserID: u.ID}}).