func runOnce(ctx context.Context, f func(context.Context) error) error { req := &pb.BeginTransactionRequest{} resp := &pb.BeginTransactionResponse{} if err := call(ctx, "beginTransaction", req, resp); err != nil { return err } subCtx := context.WithValue(ctx, ContextKey("transaction"), resp.Transaction) finished := false // Call f, rolling back the transaction if f returns a non-nil error, or panics. // The panic is not recovered. defer func() { if finished { return } finished = true // Ignore the error return value, since we are already returning a non-nil // error (or we're panicking). call(subCtx, "rollback", &pb.RollbackRequest{Transaction: resp.Transaction}, &pb.RollbackResponse{}) }() if err := f(subCtx); err != nil { return err } finished = true err := call(subCtx, "commit", &pb.CommitRequest{Transaction: resp.Transaction}, &pb.CommitResponse{}) if e, ok := err.(*errHTTP); ok && e.StatusCode == http.StatusConflict { // TODO(jbd): Make sure that we explicitly handle the case where response // has an HTTP 409 and the error message indicates that it's an concurrent // transaction error. return ErrConcurrentTransaction } return err }
func WithContext(parent context.Context, projID string, c *http.Client) context.Context { if c == nil { panic("nil *http.Client passed to WithContext") } if projID == "" { panic("empty project ID passed to WithContext") } return context.WithValue(parent, contextKey{}, &cloudContext{ ProjectID: projID, HTTPClient: c, }) }
// WithNamespace returns a new context that limits the scope its parent // context with a Datastore namespace. func WithNamespace(parent context.Context, namespace string) context.Context { return context.WithValue(parent, nsKey{}, namespace) }