Skip to content

Fix txlog open abort#100

Merged
githubzilla merged 7 commits intomainfrom
fix_txlog_open_abort
Oct 14, 2025
Merged

Fix txlog open abort#100
githubzilla merged 7 commits intomainfrom
fix_txlog_open_abort

Conversation

@githubzilla
Copy link
Collaborator

@githubzilla githubzilla commented Sep 30, 2025

max_open_files could not be -1, otherwise DB will load all files

Summary by CodeRabbit

  • Performance

    • Temporarily limits resources used during startup to reduce contention and speed opening large databases; skips some heavy consistency checks when safe.
  • Bug Fixes

    • Adds more comprehensive cleanup on multiple initialization failures to prevent partially started services and resource leaks.
  • Reliability

    • Restores safer defaults and rollbacks after failed startup steps; improves fail-fast behavior for misconfiguration or background-work errors.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 30, 2025

Walkthrough

Expands and hardens the RocksDB open sequence: caps open files before DB::Open, documents paranoid_checks context, and adds full cleanup (Close, delete/nullify db_, clear ttl_compaction_filter_) on multiple Open/follow-up failure paths; restores max_open_files and resets skip_cloud_files_in_getchildren after open.

Changes

Cohort / File(s) Summary of changes
RocksDB init & open/cleanup
eloq_data_store_service/rocksdb_cloud_data_store.cpp
Adds comments about other components affecting DB open speed; sets options.max_open_files = 128 before Open and documents paranoid_checks rationale; retains skip_cloud_files_in_getchildren = true during open then restores it after; on multiple failure paths after Open, performs full cleanup (calls Close(), delete db_, sets db_ = nullptr, clears ttl_compaction_filter_); attempts to re-enable auto-compactions and restore max_open_files to -1 via SetDBOptions, treating failures as fatal and cleaning up accordingly; adds explanatory comment when adding PurgerEventListener.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Caller
  participant DS as RocksDBCloudDataStore
  participant R as RocksDB

  Caller->>DS: StartDB() / OpenCloudDB()
  DS->>DS: set skip_cloud_files_in_getchildren = true\nset options (unordered_write, max_open_files=128)\n(comment: paranoid_checks=false rationale)
  DS->>R: DB::Open(...)
  R-->>DS: OpenStatus

  alt Open succeeds
    DS->>DS: pause background work
    alt pause ok
      DS->>DS: get current epoch
      alt epoch ok
        DS->>R: SetOptions(enable auto compactions)
        R-->>DS: SetOptionsStatus
        alt SetOptions ok
          DS->>R: SetDBOptions({"max_open_files","-1"})
          alt restore ok
            DS->>DS: restore skip_cloud_files_in_getchildren = false
            DS-->>Caller: return true
          else restore failed
            DS->>DS: Close(), delete db_, nullify pointers, clear ttl_compaction_filter_
            DS-->>Caller: return false
          end
        else SetOptions failed
          DS->>DS: Close(), delete db_, nullify pointers, clear ttl_compaction_filter_
          DS-->>Caller: return false
        end
      else epoch failed
        DS->>DS: Close(), delete db_, nullify pointers, clear ttl_compaction_filter_
        DS-->>Caller: return false
      end
    else pause failed
      DS->>DS: Close(), delete db_, nullify pointers, clear ttl_compaction_filter_
      DS-->>Caller: return false
    end
  else Open failed
    DS->>DS: Close() if needed, delete db_, nullify pointers, clear ttl_compaction_filter_
    DS-->>Caller: return false
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Suggested reviewers

  • liunyl
  • lzxddz

Poem

I'm a rabbit by the RocksDB door,
I trim the files and hush compactions more,
If opening trips or epochs lose their way,
I close the burrow and tuck the pointers away. 🐇

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The provided title “Fix txlog open abort” does not clearly describe the main change in the pull request, which focuses on capping max_open_files and enhancing cleanup logic in RocksDBCloudDataStore’s OpenCloudDB method to prevent open failures. The term “txlog” does not appear in the diff or summary, making the title misleading and unrelated to the specific modifications. Rename the title to accurately reflect the change, for example “Cap max_open_files at 128 and improve error cleanup in OpenCloudDB to prevent open failures.”
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix_txlog_open_abort

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@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

🧹 Nitpick comments (1)
eloq_data_store_service/rocksdb_cloud_data_store.cpp (1)

603-610: Consider making the temporary open file limit configurable.

The temporary cap of 32 is a reasonable workaround for the slow DB open issue, but the magic number lacks justification. Consider:

  1. Documenting why 32 was chosen (e.g., empirical testing, typical workload requirements)
  2. Making this value configurable via a parameter if different deployments have different needs
  3. Adding a constant like TEMP_MAX_OPEN_FILES_DURING_INIT for clarity

Example refactor:

+    // Temporary limit during initialization to avoid slow Open() and LRU issues
+    constexpr int TEMP_MAX_OPEN_FILES_DURING_INIT = 32;
+
     // The max_open_files default value is -1, it cause DB open all files on
     // DB::Open() This behavior causes 2 effects,
     // 1. DB::Open() will be slow
     // 2. During DB::Open, some of the opened sst files keep in LRUCache will be
     // deleted due to LRU policy, which causes DB::Open failed
     // set max_open_files to 0 will conflict with
     // skip_cloud_files_in_getchildren
-    options.max_open_files = 32;
+    options.max_open_files = TEMP_MAX_OPEN_FILES_DURING_INIT;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b3e8d0e and 6c6535d.

📒 Files selected for processing (1)
  • eloq_data_store_service/rocksdb_cloud_data_store.cpp (3 hunks)

Copy link
Contributor

@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

♻️ Duplicate comments (2)
eloq_data_store_service/rocksdb_cloud_data_store.cpp (2)

703-708: Add cleanup before returning on SetOptions failure.

Returning false here leaves the database in a partially initialized state (opened with background work resumed but auto compactions disabled), causing resource leaks and state inconsistencies.

Before returning, perform cleanup as suggested in the previous review:

     // Enable auto compactions after blocking purger
     status = db_->SetOptions({{"disable_auto_compactions", "false"}});
 
     if (!status.ok())
     {
         LOG(ERROR) << "Fail to enable auto compactions, error: "
                    << status.ToString();
+        // Clean up the partially initialized database
+        db_->Close();
+        delete db_;
+        db_ = nullptr;
+        ttl_compaction_filter_ = nullptr;
         return false;
     }

710-711: Add error handling for max_open_files restoration.

The restoration of max_open_files to its default value lacks error handling. If SetOptions fails, the database will continue running with the temporary limit of 128 open files, severely degrading performance during normal operations.

Add error handling as suggested in the previous review:

     status = db_->SetOptions(
         {{"max_open_files", "-1"}});  // restore max_open_files to default value
+
+    if (!status.ok())
+    {
+        LOG(ERROR) << "Fail to restore max_open_files to default, error: "
+                   << status.ToString();
+        // Clean up the partially initialized database
+        db_->Close();
+        delete db_;
+        db_ = nullptr;
+        ttl_compaction_filter_ = nullptr;
+        return false;
+    }
🧹 Nitpick comments (1)
eloq_data_store_service/rocksdb_cloud_data_store.cpp (1)

603-611: Consider making the temporary max_open_files limit configurable.

The hardcoded value of 128 may not be optimal for all deployment scenarios. For large-scale deployments with ample resources, this could be unnecessarily restrictive, while for resource-constrained environments, it might still be too high.

Consider adding a configuration parameter (e.g., temp_max_open_files_during_init_) with a sensible default of 128, allowing operators to tune this based on their specific environment and workload characteristics.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6c6535d and bd1e967.

📒 Files selected for processing (1)
  • eloq_data_store_service/rocksdb_cloud_data_store.cpp (3 hunks)

Copy link
Contributor

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
eloq_data_store_service/rocksdb_cloud_data_store.cpp (1)

697-702: Inconsistent cleanup pattern leaves DB in partially initialized state.

When current_epoch is empty, the code calls ContinueBackgroundWork() and returns false, but it does NOT close the database or perform the cleanup sequence used in other error paths (lines 674-679, 689-695, 716-721, 731-736). This leaves the database in a partially initialized state with background work running.

Apply this diff to use consistent cleanup:

     if (current_epoch.empty())
     {
         LOG(ERROR) << "Current epoch from db is empty";
-        db_->ContinueBackgroundWork();
+        // Clean up the partially initialized database
+        db_->Close();
+        delete db_;
+        db_ = nullptr;
+        ttl_compaction_filter_ = nullptr;
         return false;
     }
🧹 Nitpick comments (2)
eloq_data_store_service/rocksdb_cloud_data_store.cpp (2)

603-611: Consider using a named constant or configuration parameter.

The temporary max_open_files = 128 limit is hardcoded. While the rationale is sound and well-documented, consider making this value configurable or defining it as a named constant at the class or file level for easier maintenance and tuning.

Example:

// At class level or file scope
static constexpr int TEMP_MAX_OPEN_FILES_ON_DB_OPEN = 128;

// Then use:
options.max_open_files = TEMP_MAX_OPEN_FILES_ON_DB_OPEN;

689-695: Cleanup pattern is correct but duplicated.

The cleanup logic is correct and consistent. However, this exact pattern is repeated in multiple locations (lines 674-679, 689-695, 716-721, 731-736). Consider extracting it into a private helper method to reduce duplication and ensure consistency.

Example helper method:

// Add to class definition
private:
    void CleanupPartiallyInitializedDB() {
        if (db_ != nullptr) {
            db_->Close();
            delete db_;
            db_ = nullptr;
        }
        ttl_compaction_filter_ = nullptr;
    }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd1e967 and aea9208.

📒 Files selected for processing (1)
  • eloq_data_store_service/rocksdb_cloud_data_store.cpp (5 hunks)
🔇 Additional comments (3)
eloq_data_store_service/rocksdb_cloud_data_store.cpp (3)

674-679: LGTM! Proper cleanup on failure.

The cleanup sequence correctly prevents the database from being left in a partially initialized state when PauseBackgroundWork fails.


712-722: LGTM! Addresses past review comment.

The error handling and cleanup for auto compaction failures now properly closes the database and prevents partial initialization. This directly addresses the concern raised in the previous review.

Based on past review comments.


724-737: LGTM! Addresses past review comment and PR objective.

The restoration of max_open_files to -1 with proper error handling ensures that:

  1. The temporary limit of 128 is not silently left in effect (addresses past review concern)
  2. The database will use unlimited file handles after successful initialization (addresses PR objective)
  3. Any failure during restoration is properly cleaned up

Based on past review comments.

@xiexiaoy xiexiaoy self-requested a review October 9, 2025 07:18
@githubzilla githubzilla force-pushed the fix_txlog_open_abort branch 2 times, most recently from 91ec2a8 to dbc5298 Compare October 11, 2025 04:43
Copy link
Contributor

@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)
eloq_data_store_service/rocksdb_cloud_data_store.cpp (1)

606-614: Consider making the file limit configurable.

The hardcoded value of 128 for max_open_files during DB::Open is reasonable, but lacks justification for why 128 specifically. Consider making this configurable or documenting why 128 is the optimal value (e.g., based on benchmarking, typical workload patterns, or system limits).

Example:

+    // Set max_open_files to a smaller value during DB::Open to avoid issues
+    // Default value from config or 128 if not specified
+    int max_files_during_open = config.max_files_during_open > 0 
+        ? config.max_files_during_open : 128;
     // The max_open_files default value is -1, it cause DB open all files on
     // DB::Open() This behavior causes 2 effects,
     // 1. DB::Open() will be slow
     // 2. During DB::Open, some of the opened sst files keep in LRUCache will be
     // deleted due to LRU policy, which causes DB::Open failed
     // set max_open_files to 0 will conflict with
     // skip_cloud_files_in_getchildren
     // Given a smaller value here to avoid opening too many files
-    options.max_open_files = 128;
+    options.max_open_files = max_files_during_open;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91ec2a8 and dbc5298.

📒 Files selected for processing (1)
  • eloq_data_store_service/rocksdb_cloud_data_store.cpp (4 hunks)
🔇 Additional comments (3)
eloq_data_store_service/rocksdb_cloud_data_store.cpp (3)

677-682: Excellent cleanup implementation on error paths.

The consistent cleanup pattern (Close, delete, nullify pointers) across all three error paths prevents partially initialized database state and addresses the concerns raised in previous reviews. The approach ensures:

  • Proper shutdown via Close() before resource deallocation
  • Memory cleanup via delete db_
  • Prevention of use-after-free via pointer nullification
  • Consistent error handling across all failure scenarios

Based on past review comments.

Also applies to: 692-697, 718-723


702-703: Pre-existing inconsistent error handling.

Lines 702-703 contain a pre-existing error path that handles failures differently from the newly added cleanup patterns. When the current epoch is empty, it calls ContinueBackgroundWork() but doesn't perform the full cleanup sequence (Close, delete, nullify pointers) that other error paths now implement.

This could leave the database in a partially initialized state similar to the issues addressed elsewhere in this PR.

Consider whether this error path should also be updated to follow the consistent cleanup pattern:

     if (current_epoch.empty())
     {
         LOG(ERROR) << "Current epoch from db is empty";
-        db_->ContinueBackgroundWork();
+        // Clean up the partially initialized database
+        db_->Close();
+        delete db_;
+        db_ = nullptr;
+        ttl_compaction_filter_ = nullptr;
         return false;
     }

Note: This is existing code not changed in this PR, but worth reviewing for consistency.


726-739: Proper error handling for max_open_files restoration.

The SetDBOptions call correctly resets max_open_files to -1 (RocksDB’s default), and comprehensive error handling with cleanup on failure is in place.

Copy link
Contributor

@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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5fae0e6 and b90ffe5.

📒 Files selected for processing (1)
  • eloq_data_store_service/rocksdb_cloud_data_store.cpp (5 hunks)

Copy link
Contributor

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
eloq_data_store_service/rocksdb_cloud_data_store.cpp (1)

700-705: Add cleanup before returning when current_epoch is empty.

This code path resumes background work but doesn't clean up the database before returning false, leaving it in a partially initialized state. This is inconsistent with the other error paths that were fixed (lines 678-683, 693-698, 715-725).

Apply this diff to add proper cleanup:

     if (current_epoch.empty())
     {
         LOG(ERROR) << "Current epoch from db is empty";
-        db_->ContinueBackgroundWork();
+        // Clean up the partially initialized database
+        db_->Close();
+        delete db_;
+        db_ = nullptr;
+        ttl_compaction_filter_ = nullptr;
         return false;
     }
🧹 Nitpick comments (2)
eloq_data_store_service/rocksdb_cloud_data_store.cpp (2)

334-334: Remove or uncomment this line for consistency.

Line 334 is commented out, which means skip_cloud_files_in_getchildren is never set to true. However, line 669 still sets it back to false, making that code redundant. Either:

  1. Remove this commented line entirely if the optimization is no longer needed, or
  2. Uncomment it if you still want the optimization during DB::Open

The current state suggests incomplete refactoring.


448-448: Remove commented-out code.

The commented-out paranoid_checks = false line should be removed rather than left as a comment. Dead code clutters the codebase and can cause confusion.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b90ffe5 and 9daac9a.

📒 Files selected for processing (1)
  • eloq_data_store_service/rocksdb_cloud_data_store.cpp (6 hunks)
🔇 Additional comments (3)
eloq_data_store_service/rocksdb_cloud_data_store.cpp (3)

607-615: Verify the max_open_files limit of 128.

The approach of temporarily capping max_open_files to 128 during DB::Open and restoring it to -1 afterwards is sound and well-documented. The rationale addresses the PR objectives about preventing the database from loading all files.

However, verify that 128 is an appropriate limit for your use case. Consider whether this value should be:

  • Made configurable via the RocksDB config
  • Tuned based on expected workload or available system resources

678-683: LGTM! Cleanup code properly handles failure paths.

The cleanup code correctly handles partial initialization failures by:

  • Closing the database handle
  • Deleting and nullifying the db_ pointer
  • Clearing the ttl_compaction_filter_

This ensures the object is not left in a partially initialized state.

Also applies to: 693-698, 715-725


727-740: LGTM! Proper restoration of max_open_files with error handling.

The code correctly:

  • Uses SetDBOptions for the DB-wide max_open_files option
  • Restores the default value of -1 to allow unlimited file handles during normal operation
  • Includes proper error handling and cleanup if restoration fails

This completes the fix for the txlog open abort issue described in the PR objectives.

Copy link
Contributor

@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 (2)
eloq_data_store_service/rocksdb_cloud_data_store.cpp (2)

611-619: Consider making the max_open_files cap configurable.

The fix properly addresses the PR objective by capping max_open_files to 128 during DB::Open, preventing the issue where -1 causes all files to be loaded. The value is then restored to -1 after opening (lines 731-732).

However, the hard-coded value of 128 might not be optimal for all deployments. Consider making this configurable through a constructor parameter or configuration file to allow tuning based on specific deployment characteristics.


682-687: Extract duplicated cleanup logic into a helper method.

The same cleanup pattern appears in four locations (lines 682-687, 697-702, 723-728, 738-743). Each performs: db_->Close(), delete db_, db_ = nullptr, and ttl_compaction_filter_ = nullptr.

Consider extracting this into a private helper method:

void CleanupPartiallyInitializedDB()
{
    if (db_ != nullptr)
    {
        db_->Close();
        delete db_;
        db_ = nullptr;
    }
    ttl_compaction_filter_ = nullptr;
}

Then replace each cleanup block with a single call to CleanupPartiallyInitializedDB(). This reduces duplication and ensures consistency if the cleanup logic needs to be modified in the future.

Also applies to: 697-702, 723-728, 738-743

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9daac9a and 5db677a.

📒 Files selected for processing (1)
  • eloq_data_store_service/rocksdb_cloud_data_store.cpp (6 hunks)
🔇 Additional comments (7)
eloq_data_store_service/rocksdb_cloud_data_store.cpp (7)

332-334: LGTM - Enhanced documentation.

The expanded comment clearly documents the additional components involved in the DB open speed-up, providing better context for the skip_cloud_files_in_getchildren flag.


449-452: Documented tradeoff: paranoid_checks disabled during open.

The comment clearly explains that paranoid_checks is disabled because consistency checks would fail when skip_cloud_files_in_getchildren is true. Since skip_cloud_files_in_getchildren is restored to false after DB::Open completes (line 673), this tradeoff is limited to the open phase.

Based on learnings


668-673: LGTM - Proper restoration of temporary flag.

The code correctly restores skip_cloud_files_in_getchildren to false after DB::Open completes, ensuring normal operation. This pairs with the initial setting at line 335.


682-687: Proper cleanup on PauseBackgroundWork failure.

The cleanup code correctly closes the database and releases resources before returning, preventing partially initialized state.

Based on learnings


697-702: Proper cleanup on GetCurrentEpoch failure.

The cleanup code correctly closes the database and releases resources before returning, preventing partially initialized state.

Based on learnings


719-729: Proper cleanup on SetOptions failure.

The cleanup code correctly closes the database and releases resources before returning, preventing partially initialized state.

Based on learnings


731-744: LGTM - Proper restoration of max_open_files with error handling.

The code correctly restores max_open_files to its default value (-1) after successfully opening the database with the temporary limit of 128. The error handling properly cleans up resources if restoration fails, addressing previous review feedback.

This completes the fix for the PR objective: the database opens with a limited number of files (128) to avoid loading all files, then switches to unlimited files for normal operation.

Based on learnings

@githubzilla githubzilla merged commit af2bb93 into main Oct 14, 2025
1 check passed
@coderabbitai coderabbitai bot mentioned this pull request Oct 17, 2025
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.

2 participants