// executed by a single thread. so no synchronization is required. int TransactionLevelGCManager::Reclaim(const int &thread_id, const cid_t &max_cid) { int gc_counter = 0; // we delete garbage in the free list auto garbage_ctx_entry = reclaim_maps_[thread_id].begin(); while (garbage_ctx_entry != reclaim_maps_[thread_id].end()) { const cid_t garbage_ts = garbage_ctx_entry->first; auto garbage_ctx = garbage_ctx_entry->second; // if the timestamp of the garbage is older than the current max_cid, // recycle it if (garbage_ts < max_cid) { AddToRecycleMap(garbage_ctx); // Remove from the original map garbage_ctx_entry = reclaim_maps_[thread_id].erase(garbage_ctx_entry); gc_counter++; } else { // Early break since we use an ordered map break; } } LOG_TRACE("Marked %d txn contexts as recycled", gc_counter); return gc_counter; }
// this function can only be called after: // 1) All txns have exited // 2) The background gc thread has exited void GCManager::ClearGarbage() { // iterate reclaim queue and reclaim every thing because it's the end of the world now. TupleMetadata tuple_metadata; int counter = 0; while (reclaim_queue_.Dequeue(tuple_metadata) == true) { // In such case, we assume it's the end of the world and every possible // garbage is actually garbage AddToRecycleMap(tuple_metadata); counter++; } LOG_TRACE("GCManager finally recyle %d tuples", counter); }
void GCManager::Running() { // Check if we can move anything from the possibly free list to the free list. // We use a local buffer to store all possible garbage handled by this gc worker std::list<TupleMetadata> local_reclaim_queue; while (true) { std::this_thread::sleep_for( std::chrono::milliseconds(GC_PERIOD_MILLISECONDS)); LOG_TRACE("reclaim tuple thread..."); // First load every possible garbage into the list // This step move all garbage from the global reclaim queue to the worker's local queue for (size_t i = 0; i < MAX_ATTEMPT_COUNT; ++i) { TupleMetadata tuple_metadata; if (reclaim_queue_.Dequeue(tuple_metadata) == false) { break; } LOG_TRACE("Collect tuple (%u, %u) of table %u into local list", tuple_metadata.tile_group_id, tuple_metadata.tuple_slot_id, tuple_metadata.table_id); local_reclaim_queue.push_back(tuple_metadata); } // Then we go through to recycle garbage auto &txn_manager = concurrency::TransactionManagerFactory::GetInstance(); auto max_cid = txn_manager.GetMaxCommittedCid(); assert(max_cid != MAX_CID); int tuple_counter = 0; int attempt = 0; auto queue_itr = local_reclaim_queue.begin(); while (queue_itr != local_reclaim_queue.end() && attempt < MAX_ATTEMPT_COUNT) { if (queue_itr->tuple_end_cid <= max_cid) { // add the tuple to recycle map LOG_TRACE("Add tuple(%u, %u) in table %u to recycle map", queue_itr->tile_group_id, queue_itr->tuple_slot_id, queue_itr->table_id); AddToRecycleMap(*queue_itr); queue_itr = local_reclaim_queue.erase(queue_itr); tuple_counter++; } else { queue_itr++; } attempt++; } LOG_TRACE("Marked %d tuples as garbage", tuple_counter); if (is_running_ == false) { // Clear all pending garbage tuple_counter = 0; queue_itr = local_reclaim_queue.begin(); while (queue_itr != local_reclaim_queue.end()) { // In this case, we assume that no transaction is running // so every possible garbage is actually garbage AddToRecycleMap(*queue_itr); queue_itr = local_reclaim_queue.erase(queue_itr); tuple_counter ++; } LOG_TRACE("GCThread recycle last %d tuples before exits", tuple_counter); return; } } }