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
| 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 |
| 179 | void 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 |
no test coverage detected