MCPcopy Create free account
hub / github.com/Snapchat/KeyDB / endSnapshotAsync

Method endSnapshotAsync

src/snapshot.cpp:179–246  ·  view source on GitHub ↗

This function is all about minimizing the amount of work done under global lock when there has been lots of changes since snapshot creation a naive endSnapshot() will block for a very long time and will cause latency spikes. Note that this function uses a lot more CPU time than a simple endSnapshot(), we have some internal heuristics to do a synchronous endSnapshot if it makes sense

Source from the content-addressed store, hash-verified

177// Note that this function uses a lot more CPU time than a simple endSnapshot(), we
178// have some internal heuristics to do a synchronous endSnapshot if it makes sense
179void redisDbPersistentData::endSnapshotAsync(const redisDbPersistentDataSnapshot *psnapshot)
180{
181 mstime_t latency;
182
183 aeAcquireLock();
184 while (dictIsRehashing(m_pdict) || dictIsRehashing(m_pdictTombstone)) {
185 dictRehashMilliseconds(m_pdict, 1);
186 dictRehashMilliseconds(m_pdictTombstone, 1);
187 // Give someone else a chance
188 aeReleaseLock();
189 usleep(300);
190 aeAcquireLock();
191 }
192
193 latencyStartMonitor(latency);
194 if (m_pdbSnapshotASYNC && m_pdbSnapshotASYNC->m_mvccCheckpoint <= psnapshot->m_mvccCheckpoint)
195 {
196 // Free a stale async snapshot so consolidate_children can clean it up later
197 endSnapshot(m_pdbSnapshotASYNC); // FAST: just a ref decrement
198 m_pdbSnapshotASYNC = nullptr;
199 }
200
201 size_t elements = dictSize(m_pdictTombstone);
202 // if neither dict is rehashing then the merge is O(1) so don't count the size
203 if (dictIsRehashing(psnapshot->m_pdict) || dictIsRehashing(m_pdict))
204 elements += dictSize(m_pdict);
205 if (elements < c_elementsSmallLimit || psnapshot != m_spdbSnapshotHOLDER.get()) // heuristic
206 {
207 // For small snapshots it makes more sense just to merge it directly
208 endSnapshot(psnapshot);
209 latencyEndMonitor(latency);
210 latencyAddSampleIfNeeded("end-snapshot-async-synchronous-path", latency);
211 aeReleaseLock();
212 return;
213 }
214
215 // OK this is a big snapshot so lets do the merge work outside the lock
216 auto psnapshotT = createSnapshot(LLONG_MAX, false);
217 endSnapshot(psnapshot); // this will just dec the ref count since our new snapshot has a ref
218 psnapshot = nullptr;
219
220 latencyEndMonitor(latency);
221 latencyAddSampleIfNeeded("end-snapshot-async-phase-1", latency);
222 aeReleaseLock();
223
224 // do the expensive work of merging snapshots outside the ref
225 if (const_cast<redisDbPersistentDataSnapshot*>(psnapshotT)->freeTombstoneObjects(1)) // depth is one because we just creted it
226 {
227 aeAcquireLock();
228 if (m_pdbSnapshotASYNC != nullptr)
229 endSnapshot(m_pdbSnapshotASYNC);
230 m_pdbSnapshotASYNC = nullptr;
231 endSnapshot(psnapshotT);
232 aeReleaseLock();
233 return;
234 }
235
236 // Final Cleanup

Callers 5

keysCommandFunction · 0.80
rdbSaveThreadFunction · 0.80
flushStorageWeakFunction · 0.80
asyncCommandMethod · 0.80

Calls 5

aeAcquireLockFunction · 0.85
dictRehashMillisecondsFunction · 0.85
aeReleaseLockFunction · 0.85
freeTombstoneObjectsMethod · 0.80
getMethod · 0.45

Tested by

no test coverage detected