guregu/dynamoのOneとOneWithContextの違い

こんにちは、Anti-Pattern Inc.の塚本です。

Amazon DynamoDBを使った開発を最近しております。

O/Rマッパーにguregu/dynamoを使っているのですが、初めてなので非常に困惑してます!

そして、"One"と"OneWithContext"の違いについて調べた時のメモです
いまいちすっきりしませんしたが、メモとして残しておこうと思います!

それぞれのサンプルです。違いはcontextを渡すかどうか

  • One
func (s *sampleRepository) Get(ctx context.Context) (*domain.User, error) {
	var result domain.User
	if err := s.conn.Table("user").Get("id", 1).One(&result); err != nil {
		return nil, handleError(err)
	}
	return &result, nil
}
  • OneWithContext
func (s *sampleRepository) Get(ctx context.Context) (*domain.User, error) {
	var result domain.User
	if err := s.conn.Table("user").Get("id", 1).OneWithContext(ctx, &result); err != nil {
		return nil, handleError(err)
	}
	return &result, nil
}

そして、One、OneWithContextの実装
retryとGetItemWithContextの引数にContextを指定しています
https://github.com/guregu/dynamo/blob/v1.13.0/query.go

// One executes this query and retrieves a single result,
// unmarshaling the result to out.
func (q *Query) One(out interface{}) error {
	ctx, cancel := defaultContext()
	defer cancel()
	return q.OneWithContext(ctx, out)
}

func (q *Query) OneWithContext(ctx aws.Context, out interface{}) error {
	if q.err != nil {
		return q.err
	}

	// Can we use the GetItem API?
	if q.canGetItem() {
		req := q.getItemInput()

		var res *dynamodb.GetItemOutput
		err := retry(ctx, func() error {
			var err error
			res, err = q.table.db.client.GetItemWithContext(ctx, req)
			if err != nil {
				return err
			}
			if res.Item == nil {
				return ErrNotFound
			}
			return nil
		})
		if err != nil {
			return err
		}

// 抜粋

Oneの場合、空のコンテキストを使うのか。OneWithContextを呼ぶので処理は一緒っぽい

func defaultContext() (aws.Context, context.CancelFunc) {
	if RetryTimeout == 0 {
		return aws.BackgroundContext(), (func() {})
	}
	return context.WithDeadline(aws.BackgroundContext(), time.Now().Add(RetryTimeout))
}

違いはリトライ設定が必要かどうか?

func retry(ctx aws.Context, f func() error) error {
	var err error
	var next time.Duration
	b := backoff.WithContext(backoff.NewExponentialBackOff(), ctx)
	for {
		if err = f(); err == nil {
			return nil
		}

		if !canRetry(err) {
			return err
		}

		if next = b.NextBackOff(); next == backoff.Stop {
			return err
		}

		if err = aws.SleepWithContext(ctx, next); err != nil {
			return err
		}
	}
}

コードだけ見てもよく分かリませんでした。
今後、実装する時に使ってみて、動作の違いを確認しようと思います。