95 data mover keeps crashing because of something in redis adapter - #96
Conversation
There was a problem hiding this comment.
Pull request overview
This PR addresses production crashes in the redis adapter by hardening connection/reconnect behavior under concurrent access, and adds a regression test intended to reproduce the original failure mode.
Changes:
- Make
RedisConnection::connect()safe to call concurrently with other Redis operations by swappingshared_ptrclient instances under a mutex and snapshotting per-call. - Add additional synchronization in
RedisAdapteraround reader management and reconnect threading (tracked reconnect thread, shutdown flag, reader mutex, safer reader-start signaling). - Add a new concurrency stress test covering concurrent traffic + repeated
connect()calls.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
test.cpp |
Adds a RedisConnection concurrency stress test. |
RedisConnection.hpp |
Converts internal client storage to shared_ptr + snapshotting for thread-safe concurrent connect() and usage. |
RedisAdapter.hpp |
Introduces new synchronization primitives/state for reconnect and reader lifecycle management. |
RedisAdapter.cpp |
Implements shutdown-aware reconnect thread handling and adds locking around reader operations and reader start signaling. |
Comments suppressed due to low confidence (2)
RedisAdapter.cpp:174
- In addGenericReader(), subs/keyids are mutated before stopping an existing reader thread for the same token. The reader thread iterates over info.subs/info.keyids without _reader_mtx, so this ordering can cause a data race/UB if the reader is running. Stop the reader first (when token is valid), then mutate the per-token state, then restart.
uint32_t token = reader_token(key);
reader_info& info = _reader[token];
info.subs[key].push_back(make_reader_callback(func));
info.keyids[key] = "$";
RedisAdapter.cpp:292
- In add_reader_helper(), subs/keyids are updated before stopping an existing reader thread for the token. Since the reader thread reads info.subs/info.keyids without _reader_mtx, this can race if the reader is currently running. Stop the reader first (when token is valid), then update state, then restart.
uint32_t token = reader_token(key);
reader_info& info = _reader[token];
info.subs[key].push_back(func);
info.keyids[key] = "$";
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // wait until notified that thread is running (or timeout) | ||
| bool nto = cv.wait_for(lk, THREAD_START_CONFIRM) == cv_status::no_timeout; | ||
| if ( ! nto) syslog(LOG_WARNING, "start_reader timeout waiting for thread start"); | ||
| return nto; | ||
| bool nto = info.start_cv.wait_for(lk, THREAD_START_CONFIRM) == cv_status::no_timeout; | ||
| if ( ! nto) syslog(LOG_WARNING, "start_reader timeout waiting for thread start"); | ||
| return nto; |
There was a problem hiding this comment.
I don't think we've actually experienced this
| @@ -492,6 +498,12 @@ class RedisAdapter | |||
| std::unordered_map<std::string, std::string> keyids; | |||
| std::string stop; | |||
| bool run = false; | |||
| info.start_cv.notify_all(); // notify about to enter loop (NOT in loop) | ||
|
|
||
| for (Streams out; info.run; out.clear()) | ||
| { |
There was a problem hiding this comment.
I think this is addressed by making run atomic, though it's a bool, so it doesn't probably need to be but we'll do it anyway.
| atomic<bool> stop{false}; | ||
| vector<thread> users; | ||
| for (int i = 0; i < 8; i++) | ||
| { | ||
| users.emplace_back([&]() | ||
| { | ||
| while ( ! stop) | ||
| { | ||
| conn.ping(); | ||
| conn.del("redis-connection-concurrent-connect-test-key"); | ||
| } | ||
| } | ||
| ); | ||
| } | ||
|
|
||
| for (int i = 0; i < 500; i++) { conn.connect(opts); } |
There was a problem hiding this comment.
I don't care if we fix this or not
|
I'm with @wsulli : this PR is a bit beyond my paygrade. That being said two things stand out: (1) it's pretty limited and well-scoped. (2) it would slightly smaller and less complex if we dropped cluster support - should make that a priority moving forward. It's incremental improvement; LGTM once we assess AI comments for merit |
I don't understand redis adapter well enough to know if this is a good idea or not, but it does fix the crashes. I do want to see the benchmark results if those are still a thing.