Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make ledger creations atomic #703

Merged
merged 1 commit into from
Feb 27, 2025
Merged

feat: make ledger creations atomic #703

merged 1 commit into from
Feb 27, 2025

Conversation

gfyrag
Copy link
Contributor

@gfyrag gfyrag commented Feb 24, 2025

Fixes LX-3

@gfyrag gfyrag requested a review from a team as a code owner February 24, 2025 10:53
Copy link

coderabbitai bot commented Feb 24, 2025

Walkthrough

This pull request revises multiple components across the codebase. In the Justfile, the pre-commit target has been reordered. The removal of the systemstore dependency is applied in command line tools and driver initializations. Bucket factory methods, default bucket, and migration scripts are updated to use an explicit database interface (bun.IDB) and conditionally include SQL options. Ledger query capabilities are enhanced with bucket-filter support, and tests have been refactored to remove mocks and introduce concurrent operations.

Changes

File(s) Change Summary
Justfile Pre-commit target reordered to run tidy before generate, altering the command execution sequence.
cmd/buckets_upgrade.go, cmd/root.go Removed systemstore import and instantiation; updated factory and driver initialization calls to reflect dependency removal.
internal/controller/ledger/store.go Introduced ListLedgersQueryPayload type and a WithBucket method to enable bucket-based filtering in ledger queries.
internal/storage/bucket/* Updated Bucket Factory methods to accept an explicit bun.IDB parameter; modified the DefaultBucket structure, migration functions, and SQL scripts to conditionally include the concurrently keyword based on transactional context.
internal/storage/driver/* Integrated a new db *bun.DB field and removed the systemStore field; refactored driver initialization, migration handling, test mocks, and module dependency injections accordingly.
internal/storage/system/* Changed database parameter types from *bun.DB to bun.IDB; updated migrations and store query logic with bucket filtering capabilities.
test/* Adjusted transaction count in the e2e lifecycle test and refactored migration tests to remove the systemstore dependency while adopting the updated bucket factory initialization.

Sequence Diagram(s)

sequenceDiagram
    participant Dev as Developer
    participant JC as Pre-commit Target
    Dev->>JC: Trigger pre-commit checks
    JC->>JC: Run "tidy"
    JC->>JC: Run "generate"
    JC->>JC: Run "lint"
    JC->>JC: Run "export-docs-events"
    JC->>JC: Run "openapi"
    JC->>JC: Run "generate-client"
Loading
sequenceDiagram
    participant C as Client
    participant D as Driver
    participant DB as BunDB
    participant Tx as Transaction Context
    C->>D: Invoke CreateLedger()
    D->>DB: RunInTx() to start transaction
    DB->>Tx: Provide transaction context
    Tx->>D: Instantiate dynamic systemStore within Tx
    D->>Tx: Execute ledger creation within Tx
    Tx-->>D: Return result (ledger or error)
    D-->>C: Relay outcome to client
Loading

Suggested reviewers

  • flemzord
  • Dav-14

Poem

I’m a rabbit of code, hopping free,
Tweaking commands as swift as can be.
No lingering store to slow my pace,
Buckets and drivers now set the base.
With each migration, I dance with glee!

✨ Finishing Touches
  • 📝 Generate Docstrings

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

codecov bot commented Feb 24, 2025

Codecov Report

Attention: Patch coverage is 80.21978% with 18 lines in your changes missing coverage. Please review.

Project coverage is 81.85%. Comparing base (08f8e2a) to head (3dd477a).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
internal/storage/driver/driver.go 68.51% 11 Missing and 6 partials ⚠️
cmd/root.go 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #703      +/-   ##
==========================================
+ Coverage   81.74%   81.85%   +0.11%     
==========================================
  Files         135      135              
  Lines        7290     7291       +1     
==========================================
+ Hits         5959     5968       +9     
+ Misses       1025     1014      -11     
- Partials      306      309       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (1)
internal/storage/driver/buckets_generated_test.go (1)

1-6: 💡 Verification agent

🧩 Analysis chain

Verify mock generation command.

The file is auto-generated. Please ensure to run the mock generation command after merging to keep it in sync with the interface changes.

Run this command to verify mock generation:


🏁 Script executed:

#!/bin/bash
# Description: Verify mock generation command output matches the current file.

# Get the command from the file header
MOCK_CMD=$(head -n 6 internal/storage/driver/buckets_generated_test.go | grep mockgen)

# Run the command and compare output
$MOCK_CMD | diff - internal/storage/driver/buckets_generated_test.go

Length of output: 7260


Action Required: Update Verification Script for Mock Generation

The current verification script extracts the mock generation command from the header as-is—including its comment markers—which causes it to fail (e.g., "Is a directory" error) when executed. This prevents accurately verifying that the auto-generated mocks are in sync with interface changes.

Please address the following:

  • Strip Comment Markers: Update the script to remove the leading comment (//) and any extra whitespace before executing the mockgen command.
  • Re-run Verification Properly: Either modify the shell script or manually run the corrected mockgen command to ensure that the generated file content matches the expected output after interface changes.
  • Manual Check Post-Merge: As the file is auto-generated, verify that running the updated command (or a manual check) keeps the mock file in sync.
🧹 Nitpick comments (3)
internal/storage/bucket/migrations/16-create-transaction-id-index-on-moves/up.sql (1)

1-1: Consistent Templating Syntax Recommendation
The command correctly conditionally includes the concurrently keyword. However, note that the schema variable is referenced as "{{ .Schema }}" (with extra spaces) in this file, while other files use "{{.Schema}}". It is recommended to standardize the templating syntax across all migration files for consistency and to avoid any subtle parsing issues.

internal/storage/ledger/main_test.go (1)

54-55: Ensure consistent error handling style.

While the error handling is correct, consider using require.NoError(t, err) directly without the intermediate error variable assignment for consistency with the surrounding code.

-				err = bucket.GetMigrator(bunDB, "_default").Up(logging.TestingContext())
-				require.NoError(t, err)
+				require.NoError(t, bucket.GetMigrator(bunDB, "_default").Up(logging.TestingContext()))
internal/storage/bucket/default_bucket.go (1)

95-97: Helpful documentation for migration order.
Thank you for clarifying that the sequence of operations is critical to avoid deadlocks. If there are known scenarios or references about typical deadlock cases, consider adding them as examples.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bf5b817 and b5d0cc5.

⛔ Files ignored due to path filters (4)
  • go.mod is excluded by !**/*.mod
  • go.sum is excluded by !**/*.sum, !**/*.sum
  • tools/generator/go.mod is excluded by !**/*.mod
  • tools/generator/go.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (24)
  • Justfile (1 hunks)
  • cmd/buckets_upgrade.go (1 hunks)
  • cmd/root.go (1 hunks)
  • internal/controller/ledger/store.go (1 hunks)
  • internal/storage/bucket/bucket.go (1 hunks)
  • internal/storage/bucket/default_bucket.go (5 hunks)
  • internal/storage/bucket/migrations.go (1 hunks)
  • internal/storage/bucket/migrations/12-transaction-sequence-index/up.sql (1 hunks)
  • internal/storage/bucket/migrations/13-accounts-sequence-index/up.sql (1 hunks)
  • internal/storage/bucket/migrations/14-transaction-reference-index/up.sql (1 hunks)
  • internal/storage/bucket/migrations/16-create-transaction-id-index-on-moves/up.sql (1 hunks)
  • internal/storage/bucket/migrations/24-accounts-metadata-index/up.sql (1 hunks)
  • internal/storage/bucket/migrations/25-accounts-volumes-index/up.sql (1 hunks)
  • internal/storage/bucket/migrations/26-accounts-recreate-unique-index/up.sql (1 hunks)
  • internal/storage/driver/buckets_generated_test.go (2 hunks)
  • internal/storage/driver/driver.go (9 hunks)
  • internal/storage/driver/driver_test.go (1 hunks)
  • internal/storage/driver/main_test.go (1 hunks)
  • internal/storage/driver/module.go (2 hunks)
  • internal/storage/ledger/main_test.go (1 hunks)
  • internal/storage/system/migrations.go (1 hunks)
  • internal/storage/system/store.go (3 hunks)
  • test/e2e/app_lifecycle_test.go (2 hunks)
  • test/migrations/upgrade_test.go (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Tests
🔇 Additional comments (113)
internal/storage/bucket/migrations/13-accounts-sequence-index/up.sql (1)

1-1: Conditional Index Creation Based on Transaction Context
The SQL statement correctly leverages a templated conditional to include the concurrently keyword only when the migration is not running in a transactional context. This enhances flexibility when applying the migration.

internal/storage/bucket/migrations/24-accounts-metadata-index/up.sql (1)

1-1: GIN Index with Conditional Concurrent Execution
The modified statement conditionally enables concurrently for the GIN index on the accounts table using JSONB_PATH_OPS. It follows the pattern established in similar migration files. Ensure that the .Transactional variable is reliably set in your migration environment for the behavior to be as expected.

internal/storage/bucket/migrations/25-accounts-volumes-index/up.sql (1)

1-2: Efficient Use of Covering Index for Accounts Volumes
The statement conditionally applies the concurrently keyword and creates an index on the accounts_volumes table with an include clause for extra columns. This approach can help optimize query performance by creating a covering index. Please verify that the included columns (input, output) align with the anticipated query patterns for optimal performance.

internal/storage/system/migrations.go (1)

14-14: LGTM! Good abstraction improvement.

Changing from concrete type *bun.DB to interface bun.IDB improves code flexibility and testability.

Justfile (1)

6-6: LGTM! Better command ordering.

Running tidy before generate ensures all dependencies are properly organized before code generation, preventing potential issues.

test/e2e/app_lifecycle_test.go (2)

66-66: Verify if reducing test transactions maintains sufficient coverage.

The number of test transactions has been reduced from 80 to 60. While this might improve test execution time, please ensure this still provides adequate coverage for concurrent transaction handling scenarios.


126-126: LGTM! Fixed typo in comment.

Corrected "gorouting" to "goroutine" in the comment, improving code readability.

internal/storage/bucket/migrations/12-transaction-sequence-index/up.sql (1)

1-1: LGTM! Smart conditional index creation.

The conditional concurrently keyword based on .Transactional flag is a good optimization. It allows:

  • Faster index creation without table locks in non-transactional mode
  • Consistent index creation within transactions when needed
cmd/buckets_upgrade.go (1)

57-61: LGTM! Driver initialization simplified.

The removal of systemstore dependency and simplified bucket factory initialization aligns with the PR's objective of making ledger operations more atomic by reducing unnecessary database connections.

internal/storage/bucket/migrations/14-transaction-reference-index/up.sql (1)

1-1:

✅ Verification successful

Excellent modification for atomic operations!

The conditional CONCURRENTLY clause is crucial for atomic operations. PostgreSQL doesn't allow concurrent index creation within transactions, so this change correctly handles both transactional and non-transactional contexts.

Let's verify if this pattern is consistently applied across other index migrations:


🏁 Script executed:

#!/bin/bash
# Check if all index creation migrations follow the same pattern
rg -l "create.*index.*concurrently" internal/storage/bucket/migrations/

Length of output: 582


Verification: Consistent Use of Conditional Index Creation for Atomic Operations

Excellent modification! The use of the conditional CONCURRENTLY clause in internal/storage/bucket/migrations/14-transaction-reference-index/up.sql correctly prevents concurrent index creation within transactions—a crucial adjustment for atomic operations in PostgreSQL.

A quick check of the migrations shows that the concurrently keyword appears in several index creation scripts:

  • internal/storage/bucket/migrations/14-transaction-reference-index/up.sql
  • internal/storage/bucket/migrations/13-accounts-sequence-index/up.sql
  • internal/storage/bucket/migrations/12-transaction-sequence-index/up.sql
  • internal/storage/bucket/migrations/16-create-transaction-id-index-on-moves/up.sql
  • internal/storage/bucket/migrations/25-accounts-volumes-index/up.sql
  • internal/storage/bucket/migrations/24-accounts-metadata-index/up.sql
  • internal/storage/bucket/migrations/26-accounts-recreate-unique-index/up.sql

This confirms that a consistent approach is followed across our index migrations.

internal/storage/bucket/migrations/26-accounts-recreate-unique-index/up.sql (1)

1-9: Well-documented change supporting atomic operations!

The comments excellently explain the rationale behind conditional CONCURRENT index creation. This change correctly handles PostgreSQL's limitation of not allowing concurrent index creation within transaction blocks.

cmd/root.go (1)

45-45: LGTM! Consistent driver initialization.

The simplified driver initialization aligns with the changes in buckets_upgrade.go and supports the PR's objective of atomic operations.

test/migrations/upgrade_test.go (1)

68-73: LGTM! Simplified driver initialization.

The removal of the systemstore dependency and the updated bucket factory initialization aligns with making ledger creations more atomic by reducing coupling.

internal/storage/driver/main_test.go (2)

1-2: LGTM! Proper integration test setup.

The //go:build it tag correctly identifies this as an integration test file.


34-40: LGTM! Robust test environment setup.

The test setup uses proper dependency injection with deferred initialization and PostgreSQL test server creation.

internal/controller/ledger/store.go (1)

141-151: LGTM! Well-designed query enhancement.

The addition of bucket filtering capability through ListLedgersQueryPayload and WithBucket method follows good design practices:

  • Clear separation of concerns
  • Fluent interface pattern for query building
  • Type-safe implementation
internal/storage/ledger/main_test.go (1)

57-61: LGTM! Clean driver initialization.

The simplified driver initialization with the updated bucket factory aligns with the atomic ledger creation objective.

internal/storage/driver/driver_test.go (27)

1-2: Build tag usage is noted.
Looks good for marking integration tests specifically.


6-7: Imports rearranged.
No apparent issues with importing "fmt" and "github.com/formancehq/go-libs/v2/collectionutils" here.


17-18: Additional standard library imports.
Importing "math/rand" and "sync" is fine for concurrency and random number generation in tests.


22-22: New test function TestLedgersCreate.
Overall structure is sensible for creating ledgers in parallel.


26-26: Verify driver.New(db, ...) usage.
Ensure db is correctly initialized and not nil before instantiating the driver.


28-29: List of buckets and ledger count.
You defined two buckets and a count of 80 ledgers; this appears fine for concurrency testing.


53-53: Wait group usage confirmed.
Explicitly waiting for all goroutines to finish is necessary for concurrency tests.


55-55: Closing the error channel.
Correct approach to prevent further sends after goroutines are done.


57-59: Iterating over errors channel.
This pattern ensures all errors are exposed; it's a good practice in concurrency tests.


61-63: Checking minimal version.
Verifies driver state after ledger creation, looks appropriate.


65-66: Upgrading buckets.
Ensures any new or changed schema is up-to-date.


69-69: New test TestLedgersList.
Clear separation of functionalities for listing ledgers.


74-74: Reusing driver.New(...).
Consistent with previous usage; ensure no collisions between tests when running in parallel.


76-76: Generating a short random bucket name.
An 8-character slice is adequate for uniqueness in test contexts.


78-80: Creating a new ledger with configuration.
Straightforward ledger instantiation with a specified bucket.


83-84: Creating ledger successfully and requiring no error.
This pattern of creation and assertion is correct.


86-88: Second ledger instantiation.
Slight repetition, but still clear for coverage.


89-89: Error check on second ledger creation.
Confirms no error encountered.


91-92: Creating the second ledger.
Ensures coverage of repeated creation logic for separate ledger names.


94-94: Constructing the query with a bucket filter.
Correct approach for listing only relevant ledgers.


96-97: Calling ListLedgers.
Straightforward test call verifying the driver’s listing method.


99-103: Filtering obtained ledgers by bucket and checking count.
Proper validation of returned data using collectionutils.Filter.


111-111: Initializing driver in TestLedgerUpdateMetadata.
Consistent approach for test setup; usage is correct.


114-115: Creating ledger and asserting no error.
Maintains clarity in test logic.


128-128: Initializing driver in TestLedgerDeleteMetadata.
Methodologically similar to other tests; good consistency.


133-134: Creating ledger for delete metadata test.
Ensures consistent ledger setup before testing delete functionality.


136-137: Deleting ledger metadata.
Good coverage of key removal logic.

internal/storage/driver/module.go (2)

28-29: Factory creation no longer requires db.
Transitioning to a tracer-based approach fosters a more modular factory design.


45-46: Refined driver creation with explicit parameters.
Bundling db, bucketFactory, and ledgerStoreFactory in one constructor call simplifies the dependency injection flow.

Also applies to: 52-52

internal/storage/system/store.go (4)

35-35: Switched db field to bun.IDB.
Using an interface can improve testability and flexibility.


100-104: Applying bucket filter if provided.
Enhances the ListLedgers method, enabling targeted queries by bucket.


107-107: Refined pagination with ledgercontroller.PaginatedQueryOptions[ledgercontroller.ListLedgersQueryPayload].
Ensures typed payload is used for offset pagination.


128-128: New constructor New(db bun.IDB).
Aligns with the interface-based approach for the DefaultStore, promoting a more decoupled design.

internal/storage/bucket/default_bucket.go (21)

22-22: Use of bun.IDB improves flexibility.
By storing a more generic interface, the code is more testable and can accommodate transaction-scoped objects seamlessly.


82-88: Constructor aligns well with the interface approach.
Passing bun.IDB into NewDefault ensures we can use a transaction or a global DB interchangeably, enhancing testability.


106-106: Consistent reference to the transactions table.
This change ensures the correct table is referenced when creating or updating sequences.


112-114: Confirm sync mode for transaction metadata history.
Ensure "SYNC" is the intended feature flag for FeatureTransactionMetadataHistory usage across the codebase.


116-124: Trigger logic for updating transaction metadata history is sound.
The creation of a trigger after updates to transactions properly invokes update_transaction_metadata_history().


128-128: Reusing "SYNC" flag.
Double-check that the async vs. sync modes align with performance requirements.


131-139: Insert trigger for transaction metadata history.
This after-insert trigger ensures historical metadata is accurately recorded.


143-143: Feature flag for transaction accounts indexing is ON.
Indexing can improve query performance on large ledgers.


146-154: Transaction addresses trigger.
This before-insert trigger ensures any address-based logic is properly applied prior to row insertion.


158-159: Feature flags for address segments and transaction accounts.
Great for systematic enabling of advanced indexing features.


162-164: Trigger for setting transaction address segments.
This ensures the correct segmentation of addresses is applied before insertion.

Also applies to: 169-169


204-204: FeatureIndexAddressSegments enabled.
No issue noted; this is consistent with the prior usage of indexing.


207-209: Accounts address array trigger.
This before-insert trigger sets an address array for each account, likely improving lookups.

Also applies to: 214-214


219-219: FeatureMovesHistoryPostCommitEffectiveVolumes set to SYNC.
Ensure the chosen mode (SYNC) matches the performance needs and data consistency standards.


222-224: Setting effective volumes for moves.
Before-insert trigger ensures volumes are properly calculated in real-time.

Also applies to: 229-229


234-234: Same moves history feature.
No concerns; consistent with previous triggers.


237-239: Updating effective volumes after insert.
Allows for incremental updates to volumes once moves are committed.

Also applies to: 244-244


249-249: Comment clarifies logs sequence creation.
Keeping contiguous log IDs helps maintain clarity in log ordering.


250-256: Implementation of logs sequence.
Similar approach as transactions to ensure log IDs remain contiguous and consistent.


261-261: FeatureHashLogs set to SYNC.
Good for ensuring immediate hashing of logs but watch for potential overhead if logs are frequent.


264-264: Trigger for setting log hash.
Ensures logs are hashed before insertion, enhancing the tamper-proof aspect.

internal/storage/driver/driver.go (35)

12-12: Import for github.com/uptrace/bun.
Properly introduced for direct usage of Bun’s DB and transaction features.


30-30: DB field in Driver struct.
Storing *bun.DB at the driver level centralizes database management.


35-36: Migration-related fields for retries and parallelism.
These additions allow flexible configuration for bucket migrations.


41-42: Initialize local store reference and transaction.
Using RunInTx provides a clear transaction boundary for ledger creation.


43-43: Instantiate system store within the transaction.
This approach ensures ledger creation and migrations remain transactional.


45-48: Check if ledger already exists and handle constraint errors.
Nicely ensures friendly error handling with ErrLedgerAlreadyExists.


52-52: Error check for system store creation.
Properly returns an error if anything unexpected occurs.


53-53: Local error mapping.
Graceful handling of ledger errors is well-managed.


55-55: Validates if bucket is initialized.
Avoids additional migrations if the bucket was previously set up.


57-58: Check bucket’s up-to-date status.
Promptly surfaces ErrBucketOutdated if needed.


64-64: Reject usage of outdated buckets.
Prevents partial use of stale migrations.


67-68: Trigger ledger addition if bucket is already initialized.
Ensures triggers and sequences are set specifically for this new ledger.


71-71: Run migrations if bucket not yet initialized.
Automatically ensures correct schema setup for new buckets.


76-76: Attach newly created store.
Keeping a reference to ret ensures correct store usage post-transaction.


79-82: Completes transaction scope.
Rolling back on error, or committing if successful, is cleanly handled.


84-84: Return newly created ledger store.
Final step ensures the store is unified with transaction results.


89-89: Retrieve ledger from system store.
One-liner usage of systemstore.New(d.db).GetLedger.


94-94: Construct store with the bucket factory.
Combines ledger info and bucket creation in a clear step.


106-106: System store migration on driver initialization.
Guarantees the system backing tables are updated.


116-116: Create systemStore for detecting rollbacks.
Centralizes logic to handle potential schema downgrades.


118-118: Check for system schema downgrades.
Helps keep database states consistent over multiple deployments.


122-122: Fetch distinct buckets.
Used to iterate over each for potential rollback detection.


130-130: Iterate to detect downgrades.
Log-based debugging clarifies which bucket is being processed.


132-132: Run rollback checks on each bucket’s migrator.
Ensures each ledger remains at a valid migration version.


136-136: Propagate errors for rollback detection.
Prevents ignoring partial schema rollbacks.


157-157: Upgrade a single bucket by name.
Calling Migrate ensures the bucket gets the latest migrations.


162-162: Retrieve all buckets before upgrading in parallel.
Prepares for concurrent migrations across all buckets.


170-170: Submit concurrent jobs to the worker pool.
Allows parallel bucket migrations.


174-175: Create bucket instance inside each pond worker.
Ensures each upgrade job uses the correct DB handle.


181-181: Run bucket migration in a goroutine.
Allows the worker to wait for completion or retry on error.


216-216: Instantiate systemStore for minimal version checks.
Keeps system metadata queries consistent with the same DB handle.


224-224: Retrieve distinct buckets for version checks.
Ensure each bucket meets at least the minimal schema version.


230-230: Check each bucket’s minimal version.
Short-circuits if any bucket is lagging behind.


243-243: Refined New signature.
Accepting *bun.DB aligns with the rest of the code’s dependency on Bun.


249-249: Store db in constructed driver.
Central location for injection into factories.

internal/storage/bucket/bucket.go (4)

22-23: Pass db bun.IDB explicitly to Create and GetMigrator.
This change allows better control over connection lifecycles, enabling transactional contexts and mocking.


30-31: Implement Create using the NewDefault constructor.
Clear delegation of object creation; ensures consistent tracer usage.


34-35: Implement GetMigrator using the provided db bun.IDB.
Centralizes migrator retrieval without storing the DB in the factory struct.


38-39: Factory no longer holds the DB reference.
This design removes hidden state in favor of explicit parameter passing.

internal/storage/bucket/migrations.go (2)

15-35: LGTM! Good use of interface type and transactional awareness.

The changes improve the code by:

  1. Using the interface type bun.IDB instead of concrete type *bun.DB, making the function more flexible.
  2. Adding transactional awareness through type assertion and template variables.

37-37: LGTM! Consistent interface usage.

The change to use bun.IDB aligns with the interface-based approach used in GetMigrator.

internal/storage/driver/buckets_generated_test.go (1)

163-174: LGTM! Mock interfaces updated correctly.

The mock interfaces have been correctly updated to match the new method signatures with bun.IDB parameter.

Also applies to: 177-188

@gfyrag gfyrag force-pushed the ledger-atomic-creation branch 3 times, most recently from e0cc582 to 930f823 Compare February 25, 2025 12:08
@gfyrag gfyrag enabled auto-merge February 25, 2025 12:19
@gfyrag gfyrag force-pushed the ledger-atomic-creation branch from 930f823 to 8ffabef Compare February 25, 2025 12:57
@gfyrag gfyrag force-pushed the ledger-atomic-creation branch from 8ffabef to 3dd477a Compare February 27, 2025 10:19
@gfyrag gfyrag added this pull request to the merge queue Feb 27, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
internal/storage/driver/driver.go (1)

39-85: Significant improvement: Atomic ledger creation with transactions.

The CreateLedger method now wraps all operations in a transaction, ensuring that ledger creation, bucket initialization/verification, and adding the ledger to the bucket are all atomic operations. This prevents partial operations from being committed if something fails.

Consider extracting some of the error handling into separate helper functions to improve readability, especially for the repeated error wrapping patterns.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8ffabef and 3dd477a.

⛔ Files ignored due to path filters (4)
  • go.mod is excluded by !**/*.mod
  • go.sum is excluded by !**/*.sum, !**/*.sum
  • tools/generator/go.mod is excluded by !**/*.mod
  • tools/generator/go.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (24)
  • Justfile (1 hunks)
  • cmd/buckets_upgrade.go (1 hunks)
  • cmd/root.go (1 hunks)
  • internal/controller/ledger/store.go (1 hunks)
  • internal/storage/bucket/bucket.go (1 hunks)
  • internal/storage/bucket/default_bucket.go (5 hunks)
  • internal/storage/bucket/migrations.go (1 hunks)
  • internal/storage/bucket/migrations/12-transaction-sequence-index/up.sql (1 hunks)
  • internal/storage/bucket/migrations/13-accounts-sequence-index/up.sql (1 hunks)
  • internal/storage/bucket/migrations/14-transaction-reference-index/up.sql (1 hunks)
  • internal/storage/bucket/migrations/16-create-transaction-id-index-on-moves/up.sql (1 hunks)
  • internal/storage/bucket/migrations/24-accounts-metadata-index/up.sql (1 hunks)
  • internal/storage/bucket/migrations/25-accounts-volumes-index/up.sql (1 hunks)
  • internal/storage/bucket/migrations/26-accounts-recreate-unique-index/up.sql (1 hunks)
  • internal/storage/driver/buckets_generated_test.go (2 hunks)
  • internal/storage/driver/driver.go (9 hunks)
  • internal/storage/driver/driver_test.go (1 hunks)
  • internal/storage/driver/main_test.go (1 hunks)
  • internal/storage/driver/module.go (2 hunks)
  • internal/storage/ledger/main_test.go (1 hunks)
  • internal/storage/system/migrations.go (1 hunks)
  • internal/storage/system/store.go (3 hunks)
  • test/e2e/app_lifecycle_test.go (2 hunks)
  • test/migrations/upgrade_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (20)
  • internal/storage/bucket/migrations/13-accounts-sequence-index/up.sql
  • internal/storage/bucket/migrations/24-accounts-metadata-index/up.sql
  • internal/storage/bucket/migrations/26-accounts-recreate-unique-index/up.sql
  • internal/storage/bucket/migrations/12-transaction-sequence-index/up.sql
  • Justfile
  • internal/storage/system/migrations.go
  • internal/storage/bucket/migrations/16-create-transaction-id-index-on-moves/up.sql
  • internal/storage/bucket/migrations/25-accounts-volumes-index/up.sql
  • test/migrations/upgrade_test.go
  • cmd/buckets_upgrade.go
  • internal/storage/bucket/migrations/14-transaction-reference-index/up.sql
  • test/e2e/app_lifecycle_test.go
  • cmd/root.go
  • internal/storage/driver/module.go
  • internal/controller/ledger/store.go
  • internal/storage/driver/main_test.go
  • internal/storage/ledger/main_test.go
  • internal/storage/system/store.go
  • internal/storage/bucket/migrations.go
  • internal/storage/driver/buckets_generated_test.go
🧰 Additional context used
🧠 Learnings (1)
internal/storage/driver/driver_test.go (2)
Learnt from: gfyrag
PR: formancehq/ledger#703
File: internal/storage/driver/driver_test.go:31-34
Timestamp: 2025-02-24T11:31:17.135Z
Learning: In Go, using range with an integer constant (e.g., `for i := range countLedgers`) is valid syntax and compiles correctly.
Learnt from: gfyrag
PR: formancehq/ledger#703
File: internal/storage/driver/driver_test.go:35-51
Timestamp: 2025-02-24T11:31:03.968Z
Learning: Starting from Go 1.22, loop variables in goroutines are handled correctly by default - each iteration gets a fresh variable, eliminating the need for explicit local copies to avoid closure capture issues.
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Tests
🔇 Additional comments (11)
internal/storage/driver/driver_test.go (3)

22-67: Test refactoring improves concurrency testing.

The new TestLedgersCreate function effectively tests concurrent ledger creation by spawning 80 goroutines, each creating a ledger with a randomly selected bucket. The approach using a wait group and error channel to manage concurrency is well-implemented.


34-52: Goroutine implementation is correct with Go 1.22+.

Using the loop variable directly in the goroutine is safe in Go 1.22+ as each iteration gets a fresh variable.


69-104: Improved testing of bucket-based filtering.

The updated TestLedgersList function effectively tests the listing functionality with bucket filtering, creating a more direct and clear test compared to using mocks.

internal/storage/driver/driver.go (3)

28-37: Structural improvement: Direct DB access instead of system store.

Changing from storing a systemStore to having direct access to the *bun.DB gives more flexibility in how database operations are performed.


87-97: Consistent pattern for system store usage.

Creating a system store on demand using systemstore.New(d.db) establishes a consistent pattern throughout the codebase.


243-258: Updated constructor to support new DB management pattern.

The New function now takes db *bun.DB as a parameter, which aligns with the architectural changes.

internal/storage/bucket/default_bucket.go (3)

20-24: Enhanced flexibility with bun.IDB interface.

Changing from concrete *bun.DB to the interface bun.IDB improves testability and flexibility, allowing the code to work with both database connections and transactions.


82-88: Constructor updated for interface usage.

The NewDefault function now accepts bun.IDB instead of *bun.DB, which is consistent with the struct field change.


95-98: Helpful comments for preventing deadlocks.

The added comments about migration order provide important guidance to prevent deadlocks when modifying triggers and operations.

internal/storage/bucket/bucket.go (2)

21-24: Interface updated to support transaction-based operations.

The Factory interface methods now accept a db bun.IDB parameter, enabling the caller to provide either a database connection or a transaction.


26-44: Improved design: Database passed explicitly rather than stored.

Removing the db field from the DefaultFactory and passing it explicitly through method calls is a superior design. This approach:

  1. Makes dependencies clearer
  2. Supports transaction-based operations
  3. Improves testability
  4. Aligns with the dependency injection pattern

The NewDefaultFactory function has been correctly updated to remove the now-unnecessary db parameter.

Merged via the queue into main with commit a1d1b1f Feb 27, 2025
9 of 10 checks passed
@gfyrag gfyrag deleted the ledger-atomic-creation branch February 27, 2025 10:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants