Skip to content

Fix IceDisk relation table data race - #957

Merged
adsharma merged 2 commits into
LadybugDB:mainfrom
Saiteja64:fix/ice-disk-rel-table-data-race
Sep 10, 2026
Merged

Fix IceDisk relation table data race#957
adsharma merged 2 commits into
LadybugDB:mainfrom
Saiteja64:fix/ice-disk-rel-table-data-race

Conversation

@Saiteja64

Copy link
Copy Markdown
Contributor

This fixes a TSan data race in IceDiskRelTable.

indptrData is lazily initialized during parallel scans. The existing code
checked indptrData.empty() before acquiring indptrDataMutex, so that read
could race with another thread filling the vector.

Changed loadIndptrData() to acquire the existing mutex before checking or
initializing indptrData.

The full TSan suite previously reported 2 remaining races here and now reports 0.

Part of #879.

@Saiteja64
Saiteja64 force-pushed the fix/ice-disk-rel-table-data-race branch 2 times, most recently from 8321bf3 to b33bc40 Compare September 10, 2026 07:50
@fabzter

fabzter commented Sep 10, 2026

Copy link
Copy Markdown

Read through this one since it is adjacent to what I have been poking at. The race fix looks right to me — the old code read indptrData.empty() outside the lock, so a reader could observe a half-filled vector while another thread was in push_back. Taking the mutex first removes that cleanly, and the behaviour is otherwise identical (the indptrFilePath.empty() early-out still happens, just inside the lock).

Two things:

1. clang-format is red on this PR, and I think it is only the indentation. Removing the inner if (indptrData.empty()) left its body one level too deep:

    std::lock_guard lock(indptrDataMutex);
    if (!indptrFilePath.empty() && indptrData.empty()) {
        initializeIndptrReader(transaction);
            if (!indptrReader)          // <-- still indented for the removed block
                return;

Re-indenting the block by one level should make it green.

2. Worth a thought: the fast path now always takes the mutex. The double-checked pattern was
presumably there because loadIndptrData() is called from scanCSR()
(ice_disk_rel_table.cpp:318), not only from initScanState() — so on a parallel scan every
worker now serializes on indptrDataMutex for every CSR scan call, even long after the data is
loaded.

If that shows up in a scan benchmark, the usual shapes that keep both correctness and a lock-free
steady state are an atomic readiness flag with acquire/release around the existing mutex, or
std::call_once with a std::once_flag. I have not measured it, so this may well be noise
against the actual scan work — treating it as a question rather than a request. Correctness first,
and this PR gets that.

@fabzter

fabzter commented Sep 10, 2026

Copy link
Copy Markdown

Reproduced the clang-format failure locally against your branch so you do not have to guess at it — it is only the leftover indentation, nothing about the logic:

src/storage/table/ice_disk_rel_table.cpp:248:45: error: code should be clang-formatted
        initializeIndptrReader(transaction);

Removing the inner if (indptrData.empty()) left everything from if (!indptrReader) down to the
end of the block one level too deep. clang-format wants that whole body de-indented by four
spaces:

     if (!indptrFilePath.empty() && indptrData.empty()) {
         initializeIndptrReader(transaction);
-            if (!indptrReader)
-                return;
+        if (!indptrReader)
+            return;
 
-            // Initialize scan to populate column types
-            auto context = transaction->getClientContext();
+        // Initialize scan to populate column types
+        auto context = transaction->getClientContext();

and so on to the closing brace. Easiest is to just let the tool do it:

clang-format -i src/storage/table/ice_disk_rel_table.cpp

which is what CI runs via scripts/run-clang-format.py -r src/ test/ tools/ extension/. With that
applied the file is clean; the other three checks on your branch are already green.

@adsharma
adsharma force-pushed the fix/ice-disk-rel-table-data-race branch from b33bc40 to 32990ad Compare September 10, 2026 16:33
Replace the unconditional mutex in loadIndptrData with correct
double-checked locking via an atomic loaded flag, so steady-state
scans skip the mutex entirely. initializeIndptrReader now takes the
held indptrDataMutex as proof and DASSERTs ownership.
@adsharma
adsharma merged commit 392a620 into LadybugDB:main Sep 10, 2026
4 checks passed
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