void ConcurrentTableSharedStore::primeDone() { if (s_apc_file_storage.getState() != APCFileStorage::StorageState::Invalid) { s_apc_file_storage.seal(); s_apc_file_storage.hashCheck(); // Schedule the adviseOut instead of doing it immediately, so that the // initial accesses to the primed keys are not too bad. Still, for // the keys in file, a deserialization from memory is required on first // access. addToExpirationQueue(apcExtension::FileStorageFlagKey.c_str(), time(nullptr) + apcExtension::FileStorageAdviseOutPeriod); } for (auto iter = apcExtension::CompletionKeys.begin(); iter != apcExtension::CompletionKeys.end(); ++iter) { Map::accessor acc; auto const copy = strdup(iter->c_str()); if (!m_vars.insert(acc, copy)) { free(copy); return; } auto const pair = APCHandle::Create(Variant(1), false); acc->second.set(pair.handle, 0); acc->second.dataSize = pair.size; APCStats::getAPCStats().addAPCValue(pair.handle, pair.size, true); } }
// Should be called outside m_lock void ConcurrentTableSharedStore::purgeExpired() { if (m_purgeCounter.fetch_add(1, std::memory_order_relaxed) % apcExtension::PurgeFrequency != 0) { return; } time_t now = time(nullptr); int64_t oldestLive = apcExtension::UseUncounted ? HPHP::Treadmill::getOldestStartTime() : 0; ExpirationPair tmp; int i = 0; while (apcExtension::PurgeRate < 0 || i < apcExtension::PurgeRate) { if (!m_expQueue.try_pop(tmp)) { break; } if (tmp.second > now) { m_expQueue.push(tmp); break; } if (apcExtension::UseFileStorage && strcmp(tmp.first, apcExtension::FileStorageFlagKey.c_str()) == 0) { s_apc_file_storage.adviseOut(); addToExpirationQueue(apcExtension::FileStorageFlagKey.c_str(), time(nullptr) + apcExtension::FileStorageAdviseOutPeriod); continue; } m_expMap.erase(tmp.first); eraseImpl(tmp.first, true, oldestLive); free((void *)tmp.first); ++i; } }
// Should be called outside m_lock void ConcurrentTableSharedStore::purgeExpired() { if ((atomic_add(m_purgeCounter, (uint64)1) % RuntimeOption::ApcPurgeFrequency) != 0) return; time_t now = time(NULL); ExpirationPair tmp; struct timespec tsBegin, tsEnd; gettime(CLOCK_MONOTONIC, &tsBegin); int i = 0; while (RuntimeOption::ApcPurgeRate < 0 || i < RuntimeOption::ApcPurgeRate) { if (!m_expQueue.try_pop(tmp)) { break; } if (tmp.second > now) { m_expQueue.push(tmp); break; } if (RuntimeOption::ApcUseFileStorage && strcmp(tmp.first, RuntimeOption::ApcFileStorageFlagKey.c_str()) == 0) { s_apc_file_storage.adviseOut(); addToExpirationQueue(RuntimeOption::ApcFileStorageFlagKey.c_str(), time(NULL) + RuntimeOption::ApcFileStorageAdviseOutPeriod); continue; } m_expMap.erase(tmp.first); eraseImpl(tmp.first, true); free((void *)tmp.first); ++i; } gettime(CLOCK_MONOTONIC, &tsEnd); int64 elapsed = gettime_diff_us(tsBegin, tsEnd); SharedStoreStats::addPurgingTime(elapsed); // Size could be inaccurate, but for stats reporting, it is good enough SharedStoreStats::setExpireQueueSize(m_expQueue.size()); }
bool ConcurrentTableSharedStore::store(const String& key, const Variant& value, int64_t ttl, bool overwrite /* = true */, bool limit_ttl /* = true */) { StoreValue *sval; APCHandle* svar = construct(value); ConditionalReadLock l(m_lock, !apcExtension::ConcurrentTableLockFree || m_lockingFlag); const char *kcp = strdup(key.data()); bool present; time_t expiry = 0; bool overwritePrime = false; { Map::accessor acc; present = !m_vars.insert(acc, kcp); sval = &acc->second; if (present) { free((void *)kcp); if (overwrite || sval->expired()) { // if ApcTTLLimit is set, then only primed keys can have expiry == 0 overwritePrime = (sval->expiry == 0); if (sval->inMem()) { sval->var->unreferenceRoot(); } else { // mark the inFile copy invalid since we are updating the key sval->sAddr = nullptr; sval->sSize = 0; } } else { svar->unreferenceRoot(); return false; } } int64_t adjustedTtl = adjust_ttl(ttl, overwritePrime || !limit_ttl); if (check_noTTL(key.data(), key.size())) { adjustedTtl = 0; } sval->set(svar, adjustedTtl); expiry = sval->expiry; } if (expiry) { addToExpirationQueue(key.data(), expiry); } if (apcExtension::ExpireOnSets) { purgeExpired(); } return true; }
void ConcurrentTableSharedStore::primeDone() { if (s_apc_file_storage.getState() != SharedStoreFileStorage::StorageState::Invalid) { s_apc_file_storage.seal(); s_apc_file_storage.hashCheck(); // Schedule the adviseOut instead of doing it immediately, so that the // initial accesses to the primed keys are not too bad. Still, for // the keys in file, a deserialization from memory is required on first // access. addToExpirationQueue(apcExtension::FileStorageFlagKey.c_str(), time(nullptr) + apcExtension::FileStorageAdviseOutPeriod); } for (auto iter = apcExtension::CompletionKeys.begin(); iter != apcExtension::CompletionKeys.end(); ++iter) { Map::accessor acc; const char *copy = strdup(iter->c_str()); if (m_vars.insert(acc, copy)) { acc->second.set(this->construct(1), 0); } } }
// Should be called outside m_lock void ConcurrentTableSharedStore::purgeExpired() { if (m_purgeCounter.fetch_add(1, std::memory_order_relaxed) % apcExtension::PurgeFrequency != 0) { return; } time_t now = time(nullptr); ExpirationPair tmp; struct timespec tsBegin, tsEnd; Timer::GetMonotonicTime(tsBegin); int i = 0; while (apcExtension::PurgeRate < 0 || i < apcExtension::PurgeRate) { if (!m_expQueue.try_pop(tmp)) { break; } if (tmp.second > now) { m_expQueue.push(tmp); break; } if (apcExtension::UseFileStorage && strcmp(tmp.first, apcExtension::FileStorageFlagKey.c_str()) == 0) { s_apc_file_storage.adviseOut(); addToExpirationQueue(apcExtension::FileStorageFlagKey.c_str(), time(nullptr) + apcExtension::FileStorageAdviseOutPeriod); continue; } m_expMap.erase(tmp.first); eraseImpl(tmp.first, true); free((void *)tmp.first); ++i; } Timer::GetMonotonicTime(tsEnd); int64_t elapsed = gettime_diff_us(tsBegin, tsEnd); SharedStoreStats::addPurgingTime(elapsed); // Size could be inaccurate, but for stats reporting, it is good enough SharedStoreStats::setExpireQueueSize(m_expQueue.size()); }
bool ConcurrentTableSharedStore::store(CStrRef key, CVarRef val, int64 ttl, bool overwrite /* = true */) { bool stats = RuntimeOption::EnableStats && RuntimeOption::EnableAPCStats; bool statsDetail = RuntimeOption::EnableAPCSizeStats && RuntimeOption::EnableAPCSizeGroup; StoreValue *sval; SharedVariant* var = construct(key, val); ReadLock l(m_lock); const char *kcp = strdup(key.data()); bool present; time_t expiry; { Map::accessor acc; present = !m_vars.insert(acc, kcp); sval = &acc->second; if (present) { free((void *)kcp); if (overwrite || sval->expired()) { if (statsDetail) { SharedStoreStats::onDelete(key.get(), sval->var, true); } sval->var->decRef(); if (RuntimeOption::EnableAPCSizeStats && !check_skip(key.data())) { int32 size = var->getSpaceUsage(); SharedStoreStats::updateDirect(sval->size, size); sval->size = size; } } else { var->decRef(); return false; } } else { if (RuntimeOption::EnableAPCSizeStats) { int32 size = var->getSpaceUsage(); SharedStoreStats::addDirect(key.size(), size); sval->size = size; } } sval->set(var, ttl); expiry = sval->expiry; if (statsDetail) { SharedStoreStats::onStore(key.get(), var, ttl, false); } } if (RuntimeOption::ApcExpireOnSets) { if (ttl) { addToExpirationQueue(key.data(), expiry); } purgeExpired(); } if (stats) { if (present) { ServerStats::Log("apc.update", 1); } else { ServerStats::Log("apc.new", 1); if (RuntimeOption::EnableStats && RuntimeOption::EnableAPCKeyStats) { string prefix = "apc.new."; prefix += GetSkeleton(key); ServerStats::Log(prefix, 1); } } } return true; }
bool ConcurrentTableSharedStore::storeImpl(const String& key, const Variant& value, int64_t ttl, bool overwrite, bool limit_ttl) { StoreValue *sval; auto svar = APCHandle::Create(value, false); auto keyLen = key.size(); ReadLock l(m_lock); char* const kcp = strdup(key.data()); bool present; time_t expiry = 0; bool overwritePrime = false; { Map::accessor acc; APCHandle* current = nullptr; present = !m_vars.insert(acc, kcp); sval = &acc->second; if (present) { free(kcp); if (overwrite || sval->expired()) { sval->data.match( [&] (APCHandle* handle) { current = handle; // If ApcTTLLimit is set, then only primed keys can have // expire == 0. overwritePrime = sval->expire == 0; }, [&] (char*) { // Was inFile, but won't be anymore. sval->data = nullptr; sval->dataSize = 0; overwritePrime = true; } ); } else { svar.handle->unreferenceRoot(svar.size); return false; } } else { APCStats::getAPCStats().addKey(keyLen); } int64_t adjustedTtl = adjust_ttl(ttl, overwritePrime || !limit_ttl); if (check_noTTL(key.data(), key.size())) { adjustedTtl = 0; } if (current) { if (sval->expire == 0 && adjustedTtl != 0) { APCStats::getAPCStats().removeAPCValue( sval->dataSize, current, true, sval->expired()); APCStats::getAPCStats().addAPCValue(svar.handle, svar.size, false); } else { APCStats::getAPCStats().updateAPCValue( svar.handle, svar.size, current, sval->dataSize, sval->expire == 0, sval->expired()); } current->unreferenceRoot(sval->dataSize); } else { APCStats::getAPCStats().addAPCValue(svar.handle, svar.size, present); } sval->set(svar.handle, adjustedTtl); sval->dataSize = svar.size; expiry = sval->expire; } if (expiry) { addToExpirationQueue(key.data(), expiry); } if (apcExtension::ExpireOnSets) { purgeExpired(); } return true; }
bool ConcurrentTableSharedStore::store(CStrRef key, CVarRef value, int64 ttl, bool overwrite /* = true */) { StoreValue *sval; SharedVariant* svar = construct(value); ConditionalReadLock l(m_lock, !RuntimeOption::ApcConcurrentTableLockFree || m_lockingFlag); const char *kcp = strdup(key.data()); bool present; time_t expiry = 0; bool overwritePrime = false; { Map::accessor acc; present = !m_vars.insert(acc, kcp); sval = &acc->second; bool update = false; if (present) { free((void *)kcp); if (overwrite || sval->expired()) { // if ApcTTLLimit is set, then only primed keys can have expiry == 0 overwritePrime = (sval->expiry == 0); if (sval->inMem()) { stats_on_update(key.get(), sval, svar, adjust_ttl(ttl, overwritePrime)); sval->var->decRef(); update = true; } else { // mark the inFile copy invalid since we are updating the key sval->sAddr = NULL; sval->sSize = 0; } } else { svar->decRef(); return false; } } int64 adjustedTtl = adjust_ttl(ttl, overwritePrime); if (check_noTTL(key.data())) { adjustedTtl = 0; } sval->set(svar, adjustedTtl); expiry = sval->expiry; if (!update) { stats_on_add(key.get(), sval, adjustedTtl, false, false); } } if (expiry) { addToExpirationQueue(key.data(), expiry); } if (RuntimeOption::ApcExpireOnSets) { purgeExpired(); } if (present) { log_apc(std_apc_update); } else { log_apc(std_apc_new); if (RuntimeOption::EnableStats && RuntimeOption::EnableAPCKeyStats) { string prefix = "apc.new." + GetSkeleton(key); ServerStats::Log(prefix, 1); } } return true; }