Skip to content

✨ Feature Proposal: Simplify Transaction Handling with Wrapper APIs #103

@chenmingyong0423

Description

@chenmingyong0423

使用场景 | Use Case Scenario

In the official MongoDB Go driver, transaction handling requires multiple manual steps, such as session management, transaction start/commit/abort, and context propagation.
This results in verbose boilerplate code and increases the risk of errors, especially in complex workflows.

To align with mongox's goal of improving developer ergonomics, we propose introducing high-level transaction wrapper APIs to simplify these operations.

可行方案 | Feasible Solutions

// Simplified transaction handling with automatic session management
func (c *Client) RunTransaction(
	ctx context.Context,
	fn func(ctx context.Context) (any, error),
	txnOptions ...options.Lister[options.TransactionOptions],
) (any, error)

// Advanced transaction handling with manual session control
func (c *Client) WithManualTransaction(
	ctx context.Context,
	fn func(ctx context.Context, session *mongo.Session, txnOptions ...options.Lister[options.TransactionOptions]) error,
	txnOptions ...options.Lister[options.TransactionOptions],
) error

Usage Examples:

  1. RunTransaction (Automatic session management):
result, err := client.RunTransaction(ctx, func(txCtx context.Context) (any, error) {
	// Perform multiple operations within the transaction
	_, err := userColl.Creator().InsertOne(txCtx, &User{Name: "Mingyong Chen", Age: 18})
        if err != nil {
		return nil, err
	}
       
        _, err := userColl.Updater().
      		Filter(query.Id("60e96214a21b1b0001c3d69e")).
      		Updates(update.Set("name", "Mingyong Chen")).
      		UpdateOne(txCtx)
        if err != nil {
		return nil, err
	}
	return "Transaction Successful", nil
})
if err != nil {
	log.Fatalf("Transaction failed: %v", err)
}
fmt.Println("Transaction result:", result)
  1. WithManualTransaction (Manual session control):
err := client.WithManualTransaction(ctx, func(txCtx context.Context, session *mongo.Session, txnOptions ...options.Lister[options.TransactionOptions]) error {
	// Start a transaction manually
	if err = session.StartTransaction(txnOptions); err != nil {
		return err
	}

	// Perform operations
	_, err := userColl.Creator().InsertOne(txCtx, &User{Name: "Mingyong Chen", Age: 18})
        if err != nil {
		return nil, err
	}
       
      _, err := userColl.Updater().
      		Filter(query.Id("60e96214a21b1b0001c3d69e")).
      		Updates(update.Set("name", "Mingyong Chen")).
      		UpdateOne(txCtx)
        if err != nil {
		return nil, err
	}

	if err = session.CommitTransaction(txCtx); err != nil {
		return err
	}
	return nil
})
if err != nil {
	log.Fatalf("Transaction failed: %v", err)
}
fmt.Println("Transaction completed successfully")

These methods:

  • Default to writeConcern.Majority() (customizable via txnOptions).
  • Automatically manage session lifecycle.
  • Promote a clear, consistent pattern for transaction use.

This design was inspired by the official Go driver usage patterns and aims to streamline the most common transaction use cases.

其它 | Others

We’d love to gather feedback on:

  • API naming: Are RunTransaction and WithManualTransaction intuitive? Any preferred alternatives?
  • Design feedback: Does the proposed design align with your expectations for simplicity and flexibility?
  • Additional features: Are there other transaction-related features you'd like to see in mongox? For example:
    • Retry strategies for transient errors.
    • Pre/post hooks for transaction lifecycle events.
    • Metrics or logging integration for transaction monitoring.

Looking forward to community input and suggestions 🙌


If you’re interested in contributing, feel free to submit a PR!

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions