Skip to content

fix(admin): stop aborting the daemon when the TSDB flush hits a locked DB - #6004

Merged
renecannao merged 2 commits into
v3.0from
fix/tsdb-flush-sqlite-busy
Aug 9, 2026
Merged

fix(admin): stop aborting the daemon when the TSDB flush hits a locked DB#6004
renecannao merged 2 commits into
v3.0from
fix/tsdb-flush-sqlite-busy

Conversation

@renecannao

@renecannao renecannao commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

The failure

CI-mysql90-gr-g1 died during startup on an unrelated PR (#5998):

ProxySQL_Admin.cpp:3187:flush_tsdb_variables___runtime_to_database():
  [ERROR] SQLite3 error. Shutting down   rc=5 msg='database is locked'
proxysql: Assertion `0' failed.
ProxySQL_Admin::flush_tsdb_variables___runtime_to_database()
ProxySQL_Admin::init_tsdb_variables()
ProxySQL_Main_init_phase3___start_all()

rc=5 is SQLITE_BUSY. This is not a crashASSERT_SQLITE_OK() treats any
non-SQLITE_OK return as fatal and calls assert(0). A locked database is a
runtime condition (other modules read and write these databases concurrently),
not a programming error, so aborting the daemon on it is wrong by construction.

Intermittent, as expected of a lock race: the previous run of this same workflow
on the same branch passed.

Root cause

flush_tsdb_variables___runtime_to_database() was the only variables flush
that stepped its statements raw and asserted on the result. The codebase idiom is
SAFE_SQLITE3_STEP2() — 59 uses across ProxySQL_Admin.cpp and
Admin_FlushVariables.cpp — and the structurally identical sibling
flush_pgsql_variables___runtime_to_database() uses it. TSDB is newer code that
missed the convention.

It was also the only flush with no BEGIN/COMMIT, so it took and released
the write lock once per variable rather than once for the whole flush. That made
it simultaneously the flush most likely to hit SQLITE_BUSY and the only one
unable to survive it.

NDEBUG is never defined by any Makefile, so this assert is live in release
builds too — it aborted production daemons, not just CI.

Changes

1. New SQLite3DB::step_retry() (sqlite3db.{h,cpp})

Rather than reuse SAFE_SQLITE3_STEP2(), which retries forever at a fixed 100us
with no upper bound and no logging — trading a loud abort for a silent spin at
~10k wakeups/sec — this:

  • bounds the total wait (10s default)
  • backs off exponentially to a 10ms cap
  • warns once the wait passes half the budget, naming the db
  • returns the final rc so the caller decides, instead of hanging or asserting

2. flush_tsdb_variables___runtime_to_database() uses it, and wraps the whole
flush — DELETEs included — in a single transaction:

  • BEGIN is placed before the DELETEs so delete-then-reinsert is atomic and
    a mid-flush failure cannot leave tsdb-% rows deleted but not repopulated
  • on failure it rolls back and logs, leaving the previous rows intact while the
    daemon keeps running
  • the GloProxyStats == NULL early return rolls back rather than stranding an
    open transaction
  • failed steps are reset (return value discarded — it just repeats the logged
    error) so an active statement cannot hold up the ROLLBACK

Neither of the other two callers (GenericRefreshStatistics,
flush_tsdb_variables___database_to_runtime) holds an open transaction, so the
BEGIN does not nest.

Verification

Built PROXYSQL31=1 (TSDB enabled) and ran the daemon locally. All three call
paths exercised, with no assertion, rollback, or failed flush in the log:

path how result
init_tsdb_variables(), runtime=false startup 5 rows in global_variables
GenericRefreshStatistics(), runtime=true stats query 5 rows in runtime_global_variables
flush_tsdb_variables___database_to_runtime() LOAD TSDB VARIABLES TO RUNTIME / SAVE TSDB VARIABLES TO DISK clean

The runtime_global_variables rows are the meaningful check for the second
statement: init_tsdb_variables() only calls with runtime=false, so those rows
can only come from the runtime=true path committing.

Scope

Unrelated to the auth work on #5998, where the abort was merely first observed.
A sweep of the remaining ASSERT_SQLITE_OK call sites that can see contention is
worth doing separately — this PR fixes the one site that had a raw step feeding it.

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of temporary database locks during internal data updates.
    • Reduced the likelihood of update failures when the database is busy by retrying with an adaptive delay.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@renecannao, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 13 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3587e6dd-4fc5-4a69-8a0c-f2b3bac3b066

📥 Commits

Reviewing files that changed from the base of the PR and between 0390416 and ac2880d.

📒 Files selected for processing (1)
  • include/sqlite3db.h
📝 Walkthrough

Walkthrough

The PR replaces fixed SQLite retry delays with capped exponential backoff. TSDB variable writes now use the retry-capable SQLite macro for persistent and runtime statements.

Changes

SQLite retry and TSDB flush

Layer / File(s) Summary
Bounded SQLite statement retry
include/sqlite3db.h
SAFE_SQLITE3_STEP2 doubles its delay after SQLITE_LOCKED or SQLITE_BUSY, starting at 100 microseconds and capping at 10 milliseconds.
TSDB variable write retries
lib/ProxySQL_Admin.cpp
Persistent and runtime TSDB statements now use SAFE_SQLITE3_STEP2 instead of direct stepping with assertions.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

A rabbit sees locks and begins to hop,
With pauses that grow, then gently stop.
TSDB writes now retry with care,
While SQLite waits through busy air.
“Backoff,” says Bun, “keeps progress in sight!”

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main fix: preventing daemon aborts when the TSDB flush encounters a locked SQLite database.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/tsdb-flush-sqlite-busy

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (2)
lib/sqlite3db.cpp (1)

305-305: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Rename the local constant to UPPER_SNAKE_CASE.

max_backoff_us is a constant. The coding guidelines require UPPER_SNAKE_CASE for constants.

♻️ Proposed rename
-	const unsigned long max_backoff_us = 10000;
+	const unsigned long MAX_BACKOFF_US = 10000;

Update the two uses at Lines 339-340.

As per coding guidelines: "Constants and macros must use UPPER_SNAKE_CASE."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/sqlite3db.cpp` at line 305, Rename the local constant max_backoff_us to
MAX_BACKOFF_US in its declaration and update both uses in the surrounding
backoff logic, preserving the existing value and behavior.

Source: Coding guidelines

lib/ProxySQL_Admin.cpp (1)

3176-3181: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

ASSERT_SQLITE_OK now aborts while a transaction is open.

The transaction starts at Line 3154. prepare_v2 retries SQLITE_LOCKED and SQLITE_BUSY without a bound, so this path can still hang, and ASSERT_SQLITE_OK still aborts the daemon on any other error. The same applies to the ASSERT_SQLITE_OK calls on the bind and reset calls in the loop. Each abort leaves the BEGIN uncommitted.

The PR objective is to keep the daemon running when SQLite writes fail. Consider handling a non-SQLITE_OK prepare result by rolling back and returning, which matches the failure handling used for the step results.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/ProxySQL_Admin.cpp` around lines 3176 - 3181, Replace the
ASSERT_SQLITE_OK calls in the transaction around prepare_v2, bind, and reset
operations with explicit return-code handling that rolls back the open
transaction and returns on any non-SQLITE_OK result. Apply this to both query_a
and the runtime-dependent query_b paths, matching the existing step-result
failure handling and keeping the daemon running.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/ProxySQL_Admin.cpp`:
- Around line 3154-3155: Update flush_tsdb_variables___runtime_to_database to
check the bool returned by BEGIN and avoid proceeding when transaction startup
fails, preserving any caller-owned transaction. Also capture and validate the
COMMIT result, log commit failures, and execute ROLLBACK when COMMIT fails so no
transaction remains open.

In `@lib/sqlite3db.cpp`:
- Around line 308-342: Update the retry loop in step_retry to stop retrying
SQLITE_BUSY or SQLITE_LOCKED when sqlite3_get_autocommit(db) == 0, matching the
existing guarded retry loops. Preserve the current retry and timeout behavior
for autocommit mode.

---

Nitpick comments:
In `@lib/ProxySQL_Admin.cpp`:
- Around line 3176-3181: Replace the ASSERT_SQLITE_OK calls in the transaction
around prepare_v2, bind, and reset operations with explicit return-code handling
that rolls back the open transaction and returns on any non-SQLITE_OK result.
Apply this to both query_a and the runtime-dependent query_b paths, matching the
existing step-result failure handling and keeping the daemon running.

In `@lib/sqlite3db.cpp`:
- Line 305: Rename the local constant max_backoff_us to MAX_BACKOFF_US in its
declaration and update both uses in the surrounding backoff logic, preserving
the existing value and behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b239f4d3-def3-47d0-b355-804456b2c0fa

📥 Commits

Reviewing files that changed from the base of the PR and between 389929f and 8da0f42.

📒 Files selected for processing (3)
  • include/sqlite3db.h
  • lib/ProxySQL_Admin.cpp
  • lib/sqlite3db.cpp
📜 Review details
⏰ Context from checks skipped due to timeout. (6)
  • GitHub Check: CI-builds / builds (ubuntu22,-tap-mysqlx)
  • GitHub Check: CI-builds / builds (ubuntu22,-tap)
  • GitHub Check: CI-builds / builds (ubuntu24,-tap-genai-gcov)
  • GitHub Check: CI-builds / builds (debian12,-dbg)
  • GitHub Check: run / trigger
  • GitHub Check: build
🧰 Additional context used
📓 Path-based instructions (2)
include/**/*.h

📄 CodeRabbit inference engine (CLAUDE.md)

Header include guards use the #ifndef __CLASS_*_H convention.

Files:

  • include/sqlite3db.h
**/*.{cpp,h,hpp}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{cpp,h,hpp}: Class names must use PascalCase with protocol prefixes such as MySQL_, PgSQL_, and ProxySQL_.
Member variables must use snake_case.
Constants and macros must use UPPER_SNAKE_CASE.
Use C++17, and gate conditional code with #ifdef PROXYSQL31, #ifdef PROXYSQL40, #ifdef PROXYSQLFFTO, #ifdef PROXYSQLTSDB, and #ifdef PROXYSQLCLICKHOUSE; PROXYSQLGENAI must not guard core code outside plugins/genai/.
Consider performance implications when changing hot paths or other performance-critical code.
Use RAII for resource management and jemalloc for allocation.
Use pthread mutexes for synchronization and std::atomic<> for counters.

Files:

  • include/sqlite3db.h
  • lib/sqlite3db.cpp
  • lib/ProxySQL_Admin.cpp
🔇 Additional comments (2)
include/sqlite3db.h (1)

241-261: LGTM!

lib/ProxySQL_Admin.cpp (1)

3183-3187: LGTM!

Also applies to: 3190-3249

Comment thread lib/ProxySQL_Admin.cpp Outdated
Comment on lines +3154 to +3155
db->execute("BEGIN");
bool flush_ok = true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Check the result of BEGIN, and of COMMIT at Line 3242.

SQLite3DB::execute() returns bool, and this code discards it. Two failure modes follow:

  • If the connection already has an open transaction, BEGIN fails with "cannot start a transaction within a transaction". The COMMIT at Line 3242 then commits the caller's outer transaction, and the ROLLBACK at Line 3247 discards the caller's outer work. That converts a local flush failure into data loss for an unrelated write.
  • If COMMIT fails with SQLITE_BUSY, the transaction stays open on the connection after the function returns. The next call to this function then hits the nested-BEGIN case above.

Capture the return value of BEGIN, and skip the transaction wrapper or return early when it fails. Log a failed COMMIT and roll back.

#!/bin/bash
# Check whether any caller of the flush already holds an open transaction.
rg -nP -C 8 'flush_tsdb_variables___runtime_to_database\s*\(' --type=cpp
# Inspect how other flush functions in the same file manage BEGIN/COMMIT.
rg -nP -C 3 'execute\("(BEGIN|COMMIT|ROLLBACK)"\)' --type=cpp
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/ProxySQL_Admin.cpp` around lines 3154 - 3155, Update
flush_tsdb_variables___runtime_to_database to check the bool returned by BEGIN
and avoid proceeding when transaction startup fails, preserving any caller-owned
transaction. Also capture and validate the COMMIT result, log commit failures,
and execute ROLLBACK when COMMIT fails so no transaction remains open.

Comment thread lib/sqlite3db.cpp Outdated
Comment on lines +308 to +342
while (true) {
rc = (*proxy_sqlite3_step)(stmt);
if (rc != SQLITE_LOCKED && rc != SQLITE_BUSY) {
break;
}
if (waited_us >= max_wait_us) {
proxy_error(
"SQLite3 database still locked after %lums, giving up rc=%d msg='%s' db='%s'\n",
waited_us / 1000, rc, (*proxy_sqlite3_errmsg)(db), (url ? url : "(null)"));
break;
}
// Warn once, halfway through the budget. A wait this long is not
// ordinary contention and should be visible even when the retry
// ultimately succeeds and the caller reports nothing.
if (warned == false && waited_us >= max_wait_us / 2) {
proxy_warning(
"SQLite3 database locked for %lums, still retrying rc=%d db='%s'\n",
waited_us / 1000, rc, (url ? url : "(null)"));
warned = true;
}
unsigned long sleep_us = backoff_us;
if (waited_us + sleep_us > max_wait_us) {
sleep_us = max_wait_us - waited_us;
}
struct timespec ts {
.tv_sec = static_cast<time_t>(sleep_us / 1000000),
.tv_nsec = static_cast<long>((sleep_us % 1000000) * 1000)
};
nanosleep(&ts, nullptr);
waited_us += sleep_us;
backoff_us *= 2;
if (backoff_us > max_backoff_us) {
backoff_us = max_backoff_us;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Retrying SQLITE_BUSY inside an explicit transaction can block for the full budget on every statement.

The other step loops in this file check sqlite3_get_autocommit(db) and stop retrying when an explicit transaction is open (Lines 412-418 and Lines 540-546). step_retry omits that check. The only caller runs inside BEGIN/COMMIT in lib/ProxySQL_Admin.cpp. If the write lock is held by another connection, SQLite does not upgrade the lock for a statement inside an open transaction, so the retry cannot succeed. The flush then waits the full max_wait_ms for the first failing statement, and the flush blocks the calling thread for 10 seconds before it rolls back.

Consider breaking out early when sqlite3_get_autocommit(db) == 0, or document that the caller accepts the full budget per statement.

🛠️ Suggested early exit
 		rc = (*proxy_sqlite3_step)(stmt);
 		if (rc != SQLITE_LOCKED && rc != SQLITE_BUSY) {
 			break;
 		}
+		if ((*proxy_sqlite3_get_autocommit)(db) == 0) {
+			// Inside an explicit transaction the lock cannot be acquired by
+			// retrying the same statement; report the busy status instead.
+			break;
+		}
#!/bin/bash
# Find all callers of step_retry and check whether they run inside an explicit transaction.
rg -nP -C 10 '\bstep_retry\s*\(' --type=cpp --type=c
# Show existing autocommit-guarded retry loops for comparison.
rg -nP -C 4 'proxy_sqlite3_get_autocommit' --type=cpp
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/sqlite3db.cpp` around lines 308 - 342, Update the retry loop in
step_retry to stop retrying SQLITE_BUSY or SQLITE_LOCKED when
sqlite3_get_autocommit(db) == 0, matching the existing guarded retry loops.
Preserve the current retry and timeout behavior for autocommit mode.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8da0f4298a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lib/ProxySQL_Admin.cpp Outdated
// statement -- both cheaper (one fsync rather than one per variable) and a
// far smaller window in which to collide with another writer. Other
// modules do read and write these databases concurrently.
db->execute("BEGIN");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Serialize the connection-wide transaction

If this call overlaps another admin path that has already opened a transaction on the shared admindb handle (for example, dump_checksums_values_table()), SQLite rejects this nested BEGIN, but the false return from execute() is ignored. The TSDB statements then run inside the pre-existing transaction, and this function subsequently commits or rolls back that other path's work. Hold the database write lock for the entire transaction and stop if BEGIN fails.

Useful? React with 👍 / 👎.

Comment thread lib/ProxySQL_Admin.cpp Outdated
Comment on lines +3241 to +3242
if (flush_ok) {
db->execute("COMMIT");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Bound the commit retry

When flushing configdb in SQLite's default rollback-journal mode, an existing reader can allow the writes to complete but make COMMIT return SQLITE_BUSY. This calls SQLite3DB::execute(), which retries BUSY/LOCKED forever, so the newly advertised 10-second bound is bypassed and startup can remain stuck indefinitely. Commit through a bounded operation and handle failure by rolling back.

Useful? React with 👍 / 👎.

@codecov

codecov Bot commented Aug 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 53.07%. Comparing base (6159730) to head (ac2880d).
⚠️ Report is 50 commits behind head on v3.0.

Files with missing lines Patch % Lines
lib/ProxySQL_Admin.cpp 0.00% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             v3.0    #6004       +/-   ##
===========================================
+ Coverage   13.87%   53.07%   +39.20%     
===========================================
  Files         154      478      +324     
  Lines       82411   143730    +61319     
  Branches        0    36343    +36343     
===========================================
+ Hits        11431    76280    +64849     
+ Misses      70980    50546    -20434     
- Partials        0    16904    +16904     
Flag Coverage Δ
integration-tests 49.28% <0.00%> (?)
unit-tests 14.36% <0.00%> (+0.49%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…d DB

CI-mysql90-gr-g1 died during startup:

    ProxySQL_Admin.cpp:3187:flush_tsdb_variables___runtime_to_database():
      [ERROR] SQLite3 error. Shutting down   rc=5 msg='database is locked'
    proxysql: Assertion `0' failed.
    ProxySQL_Admin::flush_tsdb_variables___runtime_to_database()
    ProxySQL_Admin::init_tsdb_variables()
    ProxySQL_Main_init_phase3___start_all()

rc=5 is SQLITE_BUSY. This is not a crash: ASSERT_SQLITE_OK() treats any
non-SQLITE_OK return as fatal and calls assert(0). A locked database is a
runtime condition -- other modules read and write these databases
concurrently -- not a programming error, so aborting the daemon on it is
wrong.

flush_tsdb_variables___runtime_to_database() was the ONLY variables flush
that stepped its statements raw and asserted on the result. The codebase
idiom is SAFE_SQLITE3_STEP2(), used 59 times across ProxySQL_Admin.cpp and
Admin_FlushVariables.cpp; the structurally identical sibling
flush_pgsql_variables___runtime_to_database() uses it. TSDB is newer code
that missed the convention. Use it here too -- that is the whole fix.

NDEBUG is never defined by any Makefile, so this assert was live in release
builds too: it aborted production daemons, not just CI.

Also gives SAFE_SQLITE3_STEP2() exponential backoff, from the same 100us it
already used up to a 10ms cap. The fixed 100us sleep meant a lock held for
any noticeable time was waited out at ~10k wakeups/sec. This benefits all
59 call sites, not just this one.

Deliberately NOT done, after review: an earlier version of this patch added
a bounded step_retry() helper and wrapped the flush in BEGIN/COMMIT. Both
were dropped. The transaction put the retry inside an explicit transaction,
where SQLite will not upgrade the lock, so retrying could not succeed --
the two halves worked against each other. It also left the BEGIN and COMMIT
return values unchecked, and COMMIT went through execute(), which retries
BUSY forever and so bypassed the very bound the helper advertised. Matching
the existing idiom avoids all of it.
@renecannao
renecannao force-pushed the fix/tsdb-flush-sqlite-busy branch from 8da0f42 to 0390416 Compare August 9, 2026 11:33

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@include/sqlite3db.h`:
- Line 25: Update the _backoff_us retry backoff logic so doubling never assigns
a value above the 10,000-microsecond cap; clamp the doubled result to 10,000
before storing it, while preserving the existing growth behavior below the cap.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 71c170b7-d3bd-4101-8c33-f2d44ec5fa67

📥 Commits

Reviewing files that changed from the base of the PR and between 8da0f42 and 0390416.

📒 Files selected for processing (2)
  • include/sqlite3db.h
  • lib/ProxySQL_Admin.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/ProxySQL_Admin.cpp
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
include/**/*.h

📄 CodeRabbit inference engine (CLAUDE.md)

Header include guards use the #ifndef __CLASS_*_H convention.

Files:

  • include/sqlite3db.h
**/*.{cpp,h,hpp}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{cpp,h,hpp}: Class names must use PascalCase with protocol prefixes such as MySQL_, PgSQL_, and ProxySQL_.
Member variables must use snake_case.
Constants and macros must use UPPER_SNAKE_CASE.
Use C++17, and gate conditional code with #ifdef PROXYSQL31, #ifdef PROXYSQL40, #ifdef PROXYSQLFFTO, #ifdef PROXYSQLTSDB, and #ifdef PROXYSQLCLICKHOUSE; PROXYSQLGENAI must not guard core code outside plugins/genai/.
Consider performance implications when changing hot paths or other performance-critical code.
Use RAII for resource management and jemalloc for allocation.
Use pthread mutexes for synchronization and std::atomic<> for counters.

Files:

  • include/sqlite3db.h

Comment thread include/sqlite3db.h Outdated
Review catch. The guard tested the value BEFORE doubling:

    if (_backoff_us < 10000) { _backoff_us *= 2; }

so 6400 (which is < 10000) doubled to 12800 and stuck there, giving a real
ceiling of 12.8ms against a comment promising 10ms. Sequence was
100..3200, 6400, 12800, 12800; it is now 100..3200, 6400, 10000, 10000.

Harmless in effect, but the code should do what the comment next to it
says.
@sonarqubecloud

sonarqubecloud Bot commented Aug 9, 2026

Copy link
Copy Markdown

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.

1 participant