aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2023-12-11 10:49:23 +0100
committerStefan Boberg <[email protected]>2023-12-11 10:49:23 +0100
commit6c0412db539a2fd04ac046a586fd7fc9b426f8ef (patch)
tree3d0a8ddb706afd04eec6efb0cd2b6ca0528ed524 /src
parenthide Windows stuff (diff)
parentfix deadlock at bucket creation (#598) (diff)
downloadzen-6c0412db539a2fd04ac046a586fd7fc9b426f8ef.tar.xz
zen-6c0412db539a2fd04ac046a586fd7fc9b426f8ef.zip
Merge remote-tracking branch 'origin/main' into 273-integrated-memory-tracking
Diffstat (limited to 'src')
-rw-r--r--src/zencore/include/zencore/logging.h4
-rw-r--r--src/zencore/logging.cpp77
-rw-r--r--src/zenserver/cache/cachedisklayer.cpp347
-rw-r--r--src/zenserver/cache/cachedisklayer.h44
-rw-r--r--src/zenserver/config.cpp25
-rw-r--r--src/zenserver/config.h2
-rw-r--r--src/zenserver/diag/logging.cpp4
-rw-r--r--src/zenserver/projectstore/httpprojectstore.cpp40
-rw-r--r--src/zenserver/projectstore/httpprojectstore.h1
-rw-r--r--src/zenserver/projectstore/projectstore.cpp42
-rw-r--r--src/zenserver/projectstore/projectstore.h7
-rw-r--r--src/zenstore/compactcas.cpp4
-rw-r--r--src/zenutil/logging.cpp9
13 files changed, 400 insertions, 206 deletions
diff --git a/src/zencore/include/zencore/logging.h b/src/zencore/include/zencore/logging.h
index d14d1ab8d..8b76d754c 100644
--- a/src/zencore/include/zencore/logging.h
+++ b/src/zencore/include/zencore/logging.h
@@ -35,6 +35,10 @@ LoggerRef ErrorLog();
void SetErrorLog(std::string_view LoggerId);
LoggerRef Get(std::string_view Name);
+void ConfigureLogLevels(level::LogLevel Level, std::string_view Loggers);
+void RefreshLogLevels();
+void RefreshLogLevels(level::LogLevel DefaultLevel);
+
struct LogCategory
{
inline LogCategory(std::string_view InCategory) : CategoryName(InCategory) {}
diff --git a/src/zencore/logging.cpp b/src/zencore/logging.cpp
index 025ed4262..0bf07affd 100644
--- a/src/zencore/logging.cpp
+++ b/src/zencore/logging.cpp
@@ -4,10 +4,14 @@
#include <zencore/string.h>
#include <zencore/testing.h>
+#include <zencore/thread.h>
+ZEN_THIRD_PARTY_INCLUDES_START
+#include <spdlog/details/registry.h>
#include <spdlog/sinks/null_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/spdlog.h>
+ZEN_THIRD_PARTY_INCLUDES_END
#if ZEN_PLATFORM_WINDOWS
# pragma section(".zlog$a", read)
@@ -46,6 +50,8 @@ LoggingContext::~LoggingContext()
{
}
+//////////////////////////////////////////////////////////////////////////
+
static inline bool
IsErrorLevel(int LogLevel)
{
@@ -176,8 +182,77 @@ ToStringView(level::LogLevel Level)
} // namespace zen::logging::level
+//////////////////////////////////////////////////////////////////////////
+
namespace zen::logging {
+RwLock LogLevelsLock;
+std::string LogLevels[level::LogLevelCount];
+
+void
+ConfigureLogLevels(level::LogLevel Level, std::string_view Loggers)
+{
+ RwLock::ExclusiveLockScope _(LogLevelsLock);
+ LogLevels[Level] = Loggers;
+}
+
+void
+RefreshLogLevels(level::LogLevel* DefaultLevel)
+{
+ spdlog::details::registry::log_levels Levels;
+
+ {
+ RwLock::SharedLockScope _(LogLevelsLock);
+
+ for (int i = 0; i < level::LogLevelCount; ++i)
+ {
+ level::LogLevel CurrentLevel{i};
+
+ std::string_view Spec = LogLevels[i];
+
+ while (!Spec.empty())
+ {
+ std::string LoggerName;
+
+ if (auto CommaPos = Spec.find_first_of(','); CommaPos != std::string_view::npos)
+ {
+ LoggerName = Spec.substr(CommaPos + 1);
+ Spec.remove_prefix(CommaPos + 1);
+ }
+ else
+ {
+ LoggerName = Spec;
+ Spec = {};
+ }
+
+ Levels[LoggerName] = to_spdlog_level(CurrentLevel);
+ }
+ }
+ }
+
+ if (DefaultLevel)
+ {
+ spdlog::level::level_enum SpdDefaultLevel = to_spdlog_level(*DefaultLevel);
+ spdlog::details::registry::instance().set_levels(Levels, &SpdDefaultLevel);
+ }
+ else
+ {
+ spdlog::details::registry::instance().set_levels(Levels, nullptr);
+ }
+}
+
+void
+RefreshLogLevels(level::LogLevel DefaultLevel)
+{
+ RefreshLogLevels(&DefaultLevel);
+}
+
+void
+RefreshLogLevels()
+{
+ RefreshLogLevels(nullptr);
+}
+
void
SetLogLevel(level::LogLevel NewLogLevel)
{
@@ -240,6 +315,7 @@ Get(std::string_view Name)
if (!Logger)
{
Logger = Default().SpdLogger->clone(std::string(Name));
+ spdlog::apply_logger_env_levels(Logger);
spdlog::register_logger(Logger);
}
@@ -262,6 +338,7 @@ ConsoleLog()
if (!ConLogger)
{
ConLogger = spdlog::stdout_color_mt("console");
+ spdlog::apply_logger_env_levels(ConLogger);
ConLogger->set_pattern("%v");
}
diff --git a/src/zenserver/cache/cachedisklayer.cpp b/src/zenserver/cache/cachedisklayer.cpp
index a4a37b0af..13f3c9e58 100644
--- a/src/zenserver/cache/cachedisklayer.cpp
+++ b/src/zenserver/cache/cachedisklayer.cpp
@@ -246,7 +246,8 @@ public:
return OutVersion == CurrentDiskBucketVersion;
}
- void ParseManifest(ZenCacheDiskLayer::CacheBucket& Bucket,
+ void ParseManifest(RwLock::ExclusiveLockScope& BucketLock,
+ ZenCacheDiskLayer::CacheBucket& Bucket,
std::filesystem::path ManifestPath,
ZenCacheDiskLayer::CacheBucket::IndexMap& Index,
std::vector<AccessTime>& AccessTimes,
@@ -256,13 +257,15 @@ public:
IoBuffer MakeSidecarManifest(const Oid& BucketId, uint64_t EntryCount);
uint64_t GetSidecarSize() const { return m_ManifestEntryCount * sizeof(ManifestData); }
- void WriteSidecarFile(const std::filesystem::path& SidecarPath,
+ void WriteSidecarFile(RwLock::SharedLockScope& BucketLock,
+ const std::filesystem::path& SidecarPath,
uint64_t SnapshotLogPosition,
const ZenCacheDiskLayer::CacheBucket::IndexMap& Index,
const std::vector<AccessTime>& AccessTimes,
const std::vector<ZenCacheDiskLayer::CacheBucket::BucketPayload>& Payloads,
const std::vector<ZenCacheDiskLayer::CacheBucket::BucketMetaData>& MetaDatas);
- bool ReadSidecarFile(ZenCacheDiskLayer::CacheBucket& Bucket,
+ bool ReadSidecarFile(RwLock::ExclusiveLockScope& BucketLock,
+ ZenCacheDiskLayer::CacheBucket& Bucket,
std::filesystem::path SidecarPath,
ZenCacheDiskLayer::CacheBucket::IndexMap& Index,
std::vector<AccessTime>& AccessTimes,
@@ -309,7 +312,8 @@ private:
};
void
-BucketManifestSerializer::ParseManifest(ZenCacheDiskLayer::CacheBucket& Bucket,
+BucketManifestSerializer::ParseManifest(RwLock::ExclusiveLockScope& BucketLock,
+ ZenCacheDiskLayer::CacheBucket& Bucket,
std::filesystem::path ManifestPath,
ZenCacheDiskLayer::CacheBucket::IndexMap& Index,
std::vector<AccessTime>& AccessTimes,
@@ -317,7 +321,7 @@ BucketManifestSerializer::ParseManifest(ZenCacheDiskLayer::CacheBucket& B
{
if (Manifest["UsingMetaFile"sv].AsBool())
{
- ReadSidecarFile(Bucket, GetMetaPath(Bucket.m_BucketDir, Bucket.m_BucketName), Index, AccessTimes, Payloads);
+ ReadSidecarFile(BucketLock, Bucket, GetMetaPath(Bucket.m_BucketDir, Bucket.m_BucketName), Index, AccessTimes, Payloads);
return;
}
@@ -373,7 +377,7 @@ BucketManifestSerializer::ParseManifest(ZenCacheDiskLayer::CacheBucket& B
if (RawSize != 0 || RawHash != IoHash::Zero)
{
BucketPayload& Payload = Payloads[KeyIndex];
- Bucket.SetMetaData(Payload, BucketMetaData{.RawSize = RawSize, .RawHash = RawHash});
+ Bucket.SetMetaData(BucketLock, Payload, BucketMetaData{.RawSize = RawSize, .RawHash = RawHash});
}
}
@@ -496,7 +500,8 @@ BucketManifestSerializer::MakeSidecarManifest(const Oid& BucketId, uint64_t Entr
}
bool
-BucketManifestSerializer::ReadSidecarFile(ZenCacheDiskLayer::CacheBucket& Bucket,
+BucketManifestSerializer::ReadSidecarFile(RwLock::ExclusiveLockScope& BucketLock,
+ ZenCacheDiskLayer::CacheBucket& Bucket,
std::filesystem::path SidecarPath,
ZenCacheDiskLayer::CacheBucket::IndexMap& Index,
std::vector<AccessTime>& AccessTimes,
@@ -567,7 +572,7 @@ BucketManifestSerializer::ReadSidecarFile(ZenCacheDiskLayer::CacheBucket&
if (Entry->RawSize && Entry->RawHash != IoHash::Zero)
{
- Bucket.SetMetaData(PayloadEntry, BucketMetaData{.RawSize = Entry->RawSize, .RawHash = Entry->RawHash});
+ Bucket.SetMetaData(BucketLock, PayloadEntry, BucketMetaData{.RawSize = Entry->RawSize, .RawHash = Entry->RawHash});
}
}
@@ -580,7 +585,8 @@ BucketManifestSerializer::ReadSidecarFile(ZenCacheDiskLayer::CacheBucket&
}
void
-BucketManifestSerializer::WriteSidecarFile(const std::filesystem::path& SidecarPath,
+BucketManifestSerializer::WriteSidecarFile(RwLock::SharedLockScope&,
+ const std::filesystem::path& SidecarPath,
uint64_t SnapshotLogPosition,
const ZenCacheDiskLayer::CacheBucket::IndexMap& Index,
const std::vector<AccessTime>& AccessTimes,
@@ -696,6 +702,10 @@ ZenCacheDiskLayer::CacheBucket::OpenOrCreate(std::filesystem::path BucketDir, bo
using namespace std::literals;
ZEN_TRACE_CPU("Z$::Disk::Bucket::OpenOrCreate");
+ ZEN_ASSERT(m_IsFlushing.load());
+
+ // We want to take the lock here since we register as a GC referencer a construction
+ RwLock::ExclusiveLockScope IndexLock(m_IndexLock);
ZEN_LOG_SCOPE("opening cache bucket '{}'", BucketDir);
@@ -738,20 +748,25 @@ ZenCacheDiskLayer::CacheBucket::OpenOrCreate(std::filesystem::path BucketDir, bo
return false;
}
- InitializeIndexFromDisk(IsNew);
+ InitializeIndexFromDisk(IndexLock, IsNew);
+
+ auto _ = MakeGuard([&]() {
+ // We are now initialized, allow flushing when we exit
+ m_IsFlushing.store(false);
+ });
if (IsNew)
{
return true;
}
- ManifestReader.ParseManifest(*this, ManifestPath, m_Index, m_AccessTimes, m_Payloads);
+ ManifestReader.ParseManifest(IndexLock, *this, ManifestPath, m_Index, m_AccessTimes, m_Payloads);
return true;
}
void
-ZenCacheDiskLayer::CacheBucket::WriteIndexSnapshot(const std::function<uint64_t()>& ClaimDiskReserveFunc)
+ZenCacheDiskLayer::CacheBucket::WriteIndexSnapshotLocked(const std::function<uint64_t()>& ClaimDiskReserveFunc)
{
ZEN_TRACE_CPU("Z$::Disk::Bucket::WriteIndexSnapshot");
@@ -861,7 +876,7 @@ ZenCacheDiskLayer::CacheBucket::WriteIndexSnapshot(const std::function<uint64_t(
}
uint64_t
-ZenCacheDiskLayer::CacheBucket::ReadIndexFile(const std::filesystem::path& IndexPath, uint32_t& OutVersion)
+ZenCacheDiskLayer::CacheBucket::ReadIndexFile(RwLock::ExclusiveLockScope&, const std::filesystem::path& IndexPath, uint32_t& OutVersion)
{
ZEN_TRACE_CPU("Z$::Disk::Bucket::ReadIndexFile");
@@ -950,7 +965,7 @@ ZenCacheDiskLayer::CacheBucket::ReadIndexFile(const std::filesystem::path& Index
}
uint64_t
-ZenCacheDiskLayer::CacheBucket::ReadLog(const std::filesystem::path& LogPath, uint64_t SkipEntryCount)
+ZenCacheDiskLayer::CacheBucket::ReadLog(RwLock::ExclusiveLockScope&, const std::filesystem::path& LogPath, uint64_t SkipEntryCount)
{
ZEN_TRACE_CPU("Z$::Disk::Bucket::ReadLog");
@@ -1020,7 +1035,7 @@ ZenCacheDiskLayer::CacheBucket::ReadLog(const std::filesystem::path& LogPath, ui
};
void
-ZenCacheDiskLayer::CacheBucket::InitializeIndexFromDisk(const bool IsNew)
+ZenCacheDiskLayer::CacheBucket::InitializeIndexFromDisk(RwLock::ExclusiveLockScope& IndexLock, const bool IsNew)
{
ZEN_TRACE_CPU("Z$::Disk::Bucket::OpenLog");
@@ -1055,7 +1070,7 @@ ZenCacheDiskLayer::CacheBucket::InitializeIndexFromDisk(const bool IsNew)
if (std::filesystem::is_regular_file(IndexPath))
{
uint32_t IndexVersion = 0;
- m_LogFlushPosition = ReadIndexFile(IndexPath, IndexVersion);
+ m_LogFlushPosition = ReadIndexFile(IndexLock, IndexPath, IndexVersion);
if (IndexVersion == 0)
{
ZEN_WARN("removing invalid index file at '{}'", IndexPath);
@@ -1068,7 +1083,7 @@ ZenCacheDiskLayer::CacheBucket::InitializeIndexFromDisk(const bool IsNew)
{
if (TCasLogFile<DiskIndexEntry>::IsValid(LogPath))
{
- LogEntryCount = ReadLog(LogPath, m_LogFlushPosition);
+ LogEntryCount = ReadLog(IndexLock, LogPath, m_LogFlushPosition);
}
else if (fs::is_regular_file(LogPath))
{
@@ -1100,7 +1115,7 @@ ZenCacheDiskLayer::CacheBucket::InitializeIndexFromDisk(const bool IsNew)
if (IsNew || LogEntryCount > 0)
{
- WriteIndexSnapshot();
+ WriteIndexSnapshot(IndexLock);
}
}
@@ -1218,14 +1233,14 @@ ZenCacheDiskLayer::CacheBucket::Get(const IoHash& HashKey, ZenCacheValue& OutVal
{
ZEN_TRACE_CPU("Z$::Disk::Bucket::Get::MemCache");
OutValue.Value = IoBufferBuilder::ReadFromFileMaybe(OutValue.Value);
- RwLock::ExclusiveLockScope _(m_IndexLock);
+ RwLock::ExclusiveLockScope UpdateIndexLock(m_IndexLock);
if (auto UpdateIt = m_Index.find(HashKey); UpdateIt != m_Index.end())
{
- BucketPayload& WritePayload = m_Payloads[EntryIndex];
+ BucketPayload& WritePayload = m_Payloads[UpdateIt->second];
// Only update if it has not already been updated by other thread
if (!WritePayload.MemCached)
{
- SetMemCachedData(WritePayload, OutValue.Value);
+ SetMemCachedData(UpdateIndexLock, WritePayload, OutValue.Value);
}
}
}
@@ -1250,7 +1265,7 @@ ZenCacheDiskLayer::CacheBucket::Get(const IoHash& HashKey, ZenCacheValue& OutVal
OutValue.RawHash = IoHash::HashBuffer(OutValue.Value);
OutValue.RawSize = OutValue.Value.GetSize();
}
- RwLock::ExclusiveLockScope __(m_IndexLock);
+ RwLock::ExclusiveLockScope UpdateIndexLock(m_IndexLock);
if (auto WriteIt = m_Index.find(HashKey); WriteIt != m_Index.end())
{
BucketPayload& WritePayload = m_Payloads[WriteIt.value()];
@@ -1258,7 +1273,7 @@ ZenCacheDiskLayer::CacheBucket::Get(const IoHash& HashKey, ZenCacheValue& OutVal
// Only set if no other path has already updated the meta data
if (!WritePayload.MetaData)
{
- SetMetaData(WritePayload, {.RawSize = OutValue.RawSize, .RawHash = OutValue.RawHash});
+ SetMetaData(UpdateIndexLock, WritePayload, {.RawSize = OutValue.RawSize, .RawHash = OutValue.RawHash});
}
}
}
@@ -1297,7 +1312,7 @@ ZenCacheDiskLayer::CacheBucket::MemCacheTrim(GcClock::TimePoint ExpireTime)
{
GcClock::Tick ExpireTicks = ExpireTime.time_since_epoch().count();
- RwLock::ExclusiveLockScope _(m_IndexLock);
+ RwLock::ExclusiveLockScope IndexLock(m_IndexLock);
if (m_MemCachedPayloads.empty())
{
return;
@@ -1312,7 +1327,7 @@ ZenCacheDiskLayer::CacheBucket::MemCacheTrim(GcClock::TimePoint ExpireTime)
}
if (m_AccessTimes[Index] < ExpireTicks)
{
- RemoveMemCachedData(Payload);
+ RemoveMemCachedData(IndexLock, Payload);
}
}
m_MemCachedPayloads.shrink_to_fit();
@@ -1434,7 +1449,7 @@ ZenCacheDiskLayer::CacheBucket::SaveSnapshot(const std::function<uint64_t()>& Cl
{
RwLock::SharedLockScope IndexLock(m_IndexLock);
- WriteIndexSnapshot();
+ WriteIndexSnapshot(IndexLock);
// Note: this copy could be eliminated on shutdown to
// reduce memory usage and execution time
Index = m_Index;
@@ -1474,7 +1489,7 @@ ZenCacheDiskLayer::CacheBucket::SaveSnapshot(const std::function<uint64_t()>& Cl
else
{
RwLock::SharedLockScope IndexLock(m_IndexLock);
- WriteIndexSnapshot();
+ WriteIndexSnapshot(IndexLock);
const uint64_t EntryCount = m_Index.size();
Buffer = ManifestWriter.MakeSidecarManifest(m_BucketId, EntryCount);
uint64_t SidecarSize = ManifestWriter.GetSidecarSize();
@@ -1502,7 +1517,8 @@ ZenCacheDiskLayer::CacheBucket::SaveSnapshot(const std::function<uint64_t()>& Cl
return;
}
- ManifestWriter.WriteSidecarFile(GetMetaPath(m_BucketDir, m_BucketName),
+ ManifestWriter.WriteSidecarFile(IndexLock,
+ GetMetaPath(m_BucketDir, m_BucketName),
m_LogFlushPosition,
m_Index,
m_AccessTimes,
@@ -1776,8 +1792,8 @@ ZenCacheDiskLayer::CacheBucket::ScrubStorage(ScrubContext& Ctx)
m_StandaloneSize.fetch_sub(Location.Size(), std::memory_order::relaxed);
}
- RemoveMemCachedData(Payload);
- RemoveMetaData(Payload);
+ RemoveMemCachedData(IndexLock, Payload);
+ RemoveMetaData(IndexLock, Payload);
Location.Flags |= DiskLocation::kTombStone;
LogEntries.push_back(DiskIndexEntry{.Key = BadKey, .Location = Location});
@@ -1813,7 +1829,7 @@ ZenCacheDiskLayer::CacheBucket::ScrubStorage(ScrubContext& Ctx)
{
RwLock::ExclusiveLockScope IndexLock(m_IndexLock);
- CompactState(Payloads, AccessTimes, MetaDatas, MemCachedPayloads, FirstReferenceIndex, Index, IndexLock);
+ CompactState(IndexLock, Payloads, AccessTimes, MetaDatas, MemCachedPayloads, FirstReferenceIndex, Index, IndexLock);
}
}
}
@@ -1875,6 +1891,10 @@ ZenCacheDiskLayer::CacheBucket::GatherReferences(GcContext& GcCtx)
WriteBlockLongestTimeUs = std::max(ElapsedUs, WriteBlockLongestTimeUs);
});
#endif // CALCULATE_BLOCKING_TIME
+ if (m_Index.empty())
+ {
+ return;
+ }
Index = m_Index;
AccessTimes = m_AccessTimes;
Payloads = m_Payloads;
@@ -1954,10 +1974,9 @@ ZenCacheDiskLayer::CacheBucket::GatherReferences(GcContext& GcCtx)
for (const auto& Entry : StructuredItemsWithUnknownAttachments)
{
- const IoHash& Key = Entry.first;
- size_t PayloadIndex = Entry.second;
- BucketPayload& Payload = Payloads[PayloadIndex];
- const DiskLocation& Loc = Payload.Location;
+ const IoHash& Key = Entry.first;
+ BucketPayload& Payload = Payloads[Entry.second];
+ const DiskLocation& Loc = Payload.Location;
{
IoBuffer Buffer;
if (Loc.IsFlagSet(DiskLocation::kStandaloneFile))
@@ -1980,7 +1999,7 @@ ZenCacheDiskLayer::CacheBucket::GatherReferences(GcContext& GcCtx)
#endif // CALCULATE_BLOCKING_TIME
if (auto It = m_Index.find(Key); It != m_Index.end())
{
- const BucketPayload& CachedPayload = Payloads[PayloadIndex];
+ const BucketPayload& CachedPayload = Payloads[It->second];
if (CachedPayload.MemCached)
{
Buffer = m_MemCachedPayloads[CachedPayload.MemCached];
@@ -2098,8 +2117,6 @@ ZenCacheDiskLayer::CacheBucket::CollectGarbage(GcContext& GcCtx)
}
});
- m_SlogFile.Flush();
-
auto __ = MakeGuard([&]() {
if (!DeletedChunks.empty())
{
@@ -2118,13 +2135,20 @@ ZenCacheDiskLayer::CacheBucket::CollectGarbage(GcContext& GcCtx)
WriteBlockTimeUs += ElapsedUs;
WriteBlockLongestTimeUs = std::max(ElapsedUs, WriteBlockLongestTimeUs);
});
- CompactState(Payloads, AccessTimes, MetaDatas, MemCachedPayloads, FirstReferenceIndex, Index, IndexLock);
+ CompactState(IndexLock, Payloads, AccessTimes, MetaDatas, MemCachedPayloads, FirstReferenceIndex, Index, IndexLock);
}
GcCtx.AddDeletedCids(std::vector<IoHash>(DeletedChunks.begin(), DeletedChunks.end()));
}
});
- std::span<const IoHash> ExpiredCacheKeySpan = GcCtx.ExpiredCacheKeys(m_BucketDir.string());
+ std::span<const IoHash> ExpiredCacheKeySpan = GcCtx.ExpiredCacheKeys(m_BucketDir.string());
+ if (ExpiredCacheKeySpan.empty())
+ {
+ return;
+ }
+
+ m_SlogFile.Flush();
+
std::unordered_set<IoHash, IoHash::Hasher> ExpiredCacheKeys(ExpiredCacheKeySpan.begin(), ExpiredCacheKeySpan.end());
std::vector<DiskIndexEntry> ExpiredStandaloneEntries;
@@ -2292,7 +2316,7 @@ ZenCacheDiskLayer::CacheBucket::CollectGarbage(GcContext& GcCtx)
std::vector<DiskIndexEntry> LogEntries;
LogEntries.reserve(MovedChunks.size() + RemovedChunks.size());
{
- RwLock::ExclusiveLockScope __(m_IndexLock);
+ RwLock::ExclusiveLockScope IndexLock(m_IndexLock);
Stopwatch Timer;
const auto ____ = MakeGuard([&] {
uint64_t ElapsedUs = Timer.GetElapsedTimeUs();
@@ -2330,8 +2354,8 @@ ZenCacheDiskLayer::CacheBucket::CollectGarbage(GcContext& GcCtx)
m_Configuration.PayloadAlignment,
OldDiskLocation.GetFlags() | DiskLocation::kTombStone)});
- RemoveMemCachedData(Payload);
- RemoveMetaData(Payload);
+ RemoveMemCachedData(IndexLock, Payload);
+ RemoveMetaData(IndexLock, Payload);
m_Index.erase(ChunkHash);
DeletedChunks.insert(ChunkHash);
@@ -2368,7 +2392,7 @@ ZenCacheDiskLayer::CacheBucket::EntryCount() const
}
CacheValueDetails::ValueDetails
-ZenCacheDiskLayer::CacheBucket::GetValueDetails(const IoHash& Key, PayloadIndex Index) const
+ZenCacheDiskLayer::CacheBucket::GetValueDetails(RwLock::SharedLockScope& IndexLock, const IoHash& Key, PayloadIndex Index) const
{
std::vector<IoHash> Attachments;
const BucketPayload& Payload = m_Payloads[Index];
@@ -2380,7 +2404,7 @@ ZenCacheDiskLayer::CacheBucket::GetValueDetails(const IoHash& Key, PayloadIndex
CbObjectView Obj(Value.GetData());
Obj.IterateAttachments([&Attachments](CbFieldView Field) { Attachments.emplace_back(Field.AsAttachment()); });
}
- BucketMetaData MetaData = GetMetaData(Payload);
+ BucketMetaData MetaData = GetMetaData(IndexLock, Payload);
return CacheValueDetails::ValueDetails{.Size = Payload.Location.Size(),
.RawSize = MetaData.RawSize,
.RawHash = MetaData.RawHash,
@@ -2390,7 +2414,7 @@ ZenCacheDiskLayer::CacheBucket::GetValueDetails(const IoHash& Key, PayloadIndex
}
CacheValueDetails::BucketDetails
-ZenCacheDiskLayer::CacheBucket::GetValueDetails(const std::string_view ValueFilter) const
+ZenCacheDiskLayer::CacheBucket::GetValueDetails(RwLock::SharedLockScope& IndexLock, const std::string_view ValueFilter) const
{
CacheValueDetails::BucketDetails Details;
RwLock::SharedLockScope _(m_IndexLock);
@@ -2399,7 +2423,7 @@ ZenCacheDiskLayer::CacheBucket::GetValueDetails(const std::string_view ValueFilt
Details.Values.reserve(m_Index.size());
for (const auto& It : m_Index)
{
- Details.Values.insert_or_assign(It.first, GetValueDetails(It.first, It.second));
+ Details.Values.insert_or_assign(It.first, GetValueDetails(IndexLock, It.first, It.second));
}
}
else
@@ -2407,7 +2431,7 @@ ZenCacheDiskLayer::CacheBucket::GetValueDetails(const std::string_view ValueFilt
IoHash Key = IoHash::FromHexString(ValueFilter);
if (auto It = m_Index.find(Key); It != m_Index.end())
{
- Details.Values.insert_or_assign(It->first, GetValueDetails(It->first, It->second));
+ Details.Values.insert_or_assign(It->first, GetValueDetails(IndexLock, It->first, It->second));
}
}
return Details;
@@ -2417,10 +2441,10 @@ void
ZenCacheDiskLayer::CacheBucket::EnumerateBucketContents(
std::function<void(const IoHash& Key, const CacheValueDetails::ValueDetails& Details)>& Fn) const
{
- RwLock::SharedLockScope _(m_IndexLock);
+ RwLock::SharedLockScope IndexLock(m_IndexLock);
for (const auto& It : m_Index)
{
- CacheValueDetails::ValueDetails Vd = GetValueDetails(It.first, It.second);
+ CacheValueDetails::ValueDetails Vd = GetValueDetails(IndexLock, It.first, It.second);
Fn(It.first, Vd);
}
@@ -2595,16 +2619,16 @@ ZenCacheDiskLayer::CacheBucket::PutStandaloneCacheValue(const IoHash& HashKey, c
SetReferences(IndexLock, m_FirstReferenceIndex[EntryIndex], References);
}
m_AccessTimes[EntryIndex] = GcClock::TickCount();
- RemoveMemCachedData(Payload);
+ RemoveMemCachedData(IndexLock, Payload);
m_StandaloneSize.fetch_sub(OldSize, std::memory_order::relaxed);
}
if (Value.RawSize != 0 || Value.RawHash != IoHash::Zero)
{
- SetMetaData(m_Payloads[EntryIndex], {.RawSize = Value.RawSize, .RawHash = Value.RawHash});
+ SetMetaData(IndexLock, m_Payloads[EntryIndex], {.RawSize = Value.RawSize, .RawHash = Value.RawHash});
}
else
{
- RemoveMetaData(m_Payloads[EntryIndex]);
+ RemoveMetaData(IndexLock, m_Payloads[EntryIndex]);
}
m_SlogFile.Append({.Key = HashKey, .Location = Loc});
@@ -2612,7 +2636,9 @@ ZenCacheDiskLayer::CacheBucket::PutStandaloneCacheValue(const IoHash& HashKey, c
}
void
-ZenCacheDiskLayer::CacheBucket::SetMetaData(BucketPayload& Payload, const ZenCacheDiskLayer::CacheBucket::BucketMetaData& MetaData)
+ZenCacheDiskLayer::CacheBucket::SetMetaData(RwLock::ExclusiveLockScope&,
+ BucketPayload& Payload,
+ const ZenCacheDiskLayer::CacheBucket::BucketMetaData& MetaData)
{
if (Payload.MetaData)
{
@@ -2635,7 +2661,7 @@ ZenCacheDiskLayer::CacheBucket::SetMetaData(BucketPayload& Payload, const ZenCac
}
void
-ZenCacheDiskLayer::CacheBucket::RemoveMetaData(BucketPayload& Payload)
+ZenCacheDiskLayer::CacheBucket::RemoveMetaData(RwLock::ExclusiveLockScope&, BucketPayload& Payload)
{
if (Payload.MetaData)
{
@@ -2645,7 +2671,7 @@ ZenCacheDiskLayer::CacheBucket::RemoveMetaData(BucketPayload& Payload)
}
void
-ZenCacheDiskLayer::CacheBucket::SetMemCachedData(BucketPayload& Payload, IoBuffer& MemCachedData)
+ZenCacheDiskLayer::CacheBucket::SetMemCachedData(RwLock::ExclusiveLockScope&, BucketPayload& Payload, IoBuffer& MemCachedData)
{
uint64_t PayloadSize = MemCachedData.GetSize();
ZEN_ASSERT(PayloadSize != 0);
@@ -2670,7 +2696,7 @@ ZenCacheDiskLayer::CacheBucket::SetMemCachedData(BucketPayload& Payload, IoBuffe
}
size_t
-ZenCacheDiskLayer::CacheBucket::RemoveMemCachedData(BucketPayload& Payload)
+ZenCacheDiskLayer::CacheBucket::RemoveMemCachedData(RwLock::ExclusiveLockScope&, BucketPayload& Payload)
{
if (Payload.MemCached)
{
@@ -2685,7 +2711,7 @@ ZenCacheDiskLayer::CacheBucket::RemoveMemCachedData(BucketPayload& Payload)
}
ZenCacheDiskLayer::CacheBucket::BucketMetaData
-ZenCacheDiskLayer::CacheBucket::GetMetaData(const BucketPayload& Payload) const
+ZenCacheDiskLayer::CacheBucket::GetMetaData(RwLock::SharedLockScope&, const BucketPayload& Payload) const
{
if (Payload.MetaData)
{
@@ -2728,8 +2754,8 @@ ZenCacheDiskLayer::CacheBucket::PutInlineCacheValue(const IoHash& HashKey, const
ZEN_ASSERT_SLOW(EntryIndex < PayloadIndex(m_AccessTimes.size()));
BucketPayload& Payload = m_Payloads[EntryIndex];
- RemoveMemCachedData(Payload);
- RemoveMetaData(Payload);
+ RemoveMemCachedData(IndexLock, Payload);
+ RemoveMetaData(IndexLock, Payload);
Payload = (BucketPayload{.Location = Location});
m_AccessTimes[EntryIndex] = GcClock::TickCount();
@@ -3027,6 +3053,10 @@ ZenCacheDiskLayer::CacheBucket::RemoveExpiredData(GcCtx& Ctx, GcStats& Stats)
{
return nullptr;
}
+ if (m_Index.empty())
+ {
+ return nullptr;
+ }
TotalEntries = m_Index.size();
@@ -3072,8 +3102,8 @@ ZenCacheDiskLayer::CacheBucket::RemoveExpiredData(GcCtx& Ctx, GcStats& Stats)
auto It = m_Index.find(Entry.Key);
ZEN_ASSERT(It != m_Index.end());
BucketPayload& Payload = m_Payloads[It->second];
- RemoveMetaData(Payload);
- Stats.FreedMemory += RemoveMemCachedData(Payload);
+ RemoveMetaData(IndexLock, Payload);
+ Stats.FreedMemory += RemoveMemCachedData(IndexLock, Payload);
m_Index.erase(It);
Stats.DeletedCount++;
}
@@ -3092,7 +3122,7 @@ ZenCacheDiskLayer::CacheBucket::RemoveExpiredData(GcCtx& Ctx, GcStats& Stats)
IndexMap Index;
{
RwLock::ExclusiveLockScope IndexLock(m_IndexLock);
- CompactState(Payloads, AccessTimes, MetaDatas, MemCachedPayloads, FirstReferenceIndex, Index, IndexLock);
+ CompactState(IndexLock, Payloads, AccessTimes, MetaDatas, MemCachedPayloads, FirstReferenceIndex, Index, IndexLock);
}
}
@@ -3486,6 +3516,14 @@ ZenCacheDiskLayer::CacheBucket::CreateReferenceCheckers(GcCtx& Ctx)
ZEN_INFO("GCV2: cachebucket [CREATE CHECKERS] '{}': completed in {}", m_BucketDir, NiceTimeSpanMs(Timer.GetElapsedTimeMs()));
});
+ {
+ RwLock::SharedLockScope __(m_IndexLock);
+ if (m_Index.empty())
+ {
+ return {};
+ }
+ }
+
return {new DiskBucketReferenceChecker(*this)};
}
@@ -3666,7 +3704,8 @@ ZenCacheDiskLayer::CacheBucket::ClearReferenceCache()
}
void
-ZenCacheDiskLayer::CacheBucket::CompactState(std::vector<BucketPayload>& Payloads,
+ZenCacheDiskLayer::CacheBucket::CompactState(RwLock::ExclusiveLockScope&,
+ std::vector<BucketPayload>& Payloads,
std::vector<AccessTime>& AccessTimes,
std::vector<BucketMetaData>& MetaDatas,
std::vector<IoBuffer>& MemCachedPayloads,
@@ -3749,124 +3788,99 @@ ZenCacheDiskLayer::ZenCacheDiskLayer(GcManager& Gc, JobQueue& JobQueue, const st
ZenCacheDiskLayer::~ZenCacheDiskLayer()
{
-}
-
-bool
-ZenCacheDiskLayer::Get(std::string_view InBucket, const IoHash& HashKey, ZenCacheValue& OutValue)
-{
- ZEN_TRACE_CPU("Z$::Disk::Get");
-
- const auto BucketName = std::string(InBucket);
- CacheBucket* Bucket = nullptr;
-
+ try
{
- RwLock::SharedLockScope _(m_Lock);
-
- auto It = m_Buckets.find(BucketName);
-
- if (It != m_Buckets.end())
{
- Bucket = It->second.get();
+ RwLock::ExclusiveLockScope _(m_Lock);
+ for (auto& It : m_Buckets)
+ {
+ m_DroppedBuckets.emplace_back(std::move(It.second));
+ }
+ m_Buckets.clear();
}
+ // We destroy the buckets without holding a lock since destructor calls GcManager::RemoveGcReferencer which takes an exclusive lock.
+ // This can cause a deadlock, if GC is running we would block while holding ZenCacheDiskLayer::m_Lock
+ m_DroppedBuckets.clear();
}
-
- if (Bucket == nullptr)
+ catch (std::exception& Ex)
{
- // Bucket needs to be opened/created
+ ZEN_ERROR("~ZenCacheDiskLayer() failed. Reason: '{}'", Ex.what());
+ }
+}
- RwLock::ExclusiveLockScope _(m_Lock);
+ZenCacheDiskLayer::CacheBucket*
+ZenCacheDiskLayer::GetOrCreateBucket(std::string_view InBucket)
+{
+ ZEN_TRACE_CPU("Z$::Disk::GetOrCreateBucket");
+ const auto BucketName = std::string(InBucket);
+ {
+ RwLock::SharedLockScope SharedLock(m_Lock);
if (auto It = m_Buckets.find(BucketName); It != m_Buckets.end())
{
- Bucket = It->second.get();
- }
- else
- {
- auto InsertResult =
- m_Buckets.emplace(BucketName,
- std::make_unique<CacheBucket>(m_Gc, m_TotalMemCachedSize, BucketName, m_Configuration.BucketConfig));
- Bucket = InsertResult.first->second.get();
-
- std::filesystem::path BucketPath = m_RootDir;
- BucketPath /= BucketName;
-
- if (!Bucket->OpenOrCreate(BucketPath))
- {
- m_Buckets.erase(InsertResult.first);
- return false;
- }
+ return It->second.get();
}
}
- ZEN_ASSERT(Bucket != nullptr);
- if (Bucket->Get(HashKey, OutValue))
+ // We create the bucket without holding a lock since contructor calls GcManager::AddGcReferencer which takes an exclusive lock.
+ // This can cause a deadlock, if GC is running we would block while holding ZenCacheDiskLayer::m_Lock
+ std::unique_ptr<CacheBucket> Bucket(
+ std::make_unique<CacheBucket>(m_Gc, m_TotalMemCachedSize, BucketName, m_Configuration.BucketConfig));
+
+ RwLock::ExclusiveLockScope Lock(m_Lock);
+ if (auto It = m_Buckets.find(BucketName); It != m_Buckets.end())
{
- TryMemCacheTrim();
- return true;
+ return It->second.get();
}
- return false;
-}
-
-void
-ZenCacheDiskLayer::Put(std::string_view InBucket, const IoHash& HashKey, const ZenCacheValue& Value, std::span<IoHash> References)
-{
- ZEN_TRACE_CPU("Z$::Disk::Put");
-
- const auto BucketName = std::string(InBucket);
- CacheBucket* Bucket = nullptr;
+ std::filesystem::path BucketPath = m_RootDir;
+ BucketPath /= BucketName;
+ try
{
- RwLock::SharedLockScope _(m_Lock);
-
- auto It = m_Buckets.find(BucketName);
-
- if (It != m_Buckets.end())
+ if (!Bucket->OpenOrCreate(BucketPath))
{
- Bucket = It->second.get();
+ ZEN_WARN("Found directory '{}' in our base directory '{}' but it is not a valid bucket", BucketName, m_RootDir);
+ return nullptr;
}
}
-
- if (Bucket == nullptr)
+ catch (const std::exception& Err)
{
- // New bucket needs to be created
+ ZEN_WARN("Creating bucket '{}' in '{}' FAILED, reason: '{}'", BucketName, BucketPath, Err.what());
+ throw;
+ }
- RwLock::ExclusiveLockScope _(m_Lock);
+ CacheBucket* Result = Bucket.get();
+ m_Buckets.emplace(BucketName, std::move(Bucket));
- if (auto It = m_Buckets.find(BucketName); It != m_Buckets.end())
- {
- Bucket = It->second.get();
- }
- else
- {
- auto InsertResult =
- m_Buckets.emplace(BucketName,
- std::make_unique<CacheBucket>(m_Gc, m_TotalMemCachedSize, BucketName, m_Configuration.BucketConfig));
- Bucket = InsertResult.first->second.get();
+ return Result;
+}
- std::filesystem::path BucketPath = m_RootDir;
- BucketPath /= BucketName;
+bool
+ZenCacheDiskLayer::Get(std::string_view InBucket, const IoHash& HashKey, ZenCacheValue& OutValue)
+{
+ ZEN_TRACE_CPU("Z$::Disk::Get");
- try
- {
- if (!Bucket->OpenOrCreate(BucketPath))
- {
- ZEN_WARN("Found directory '{}' in our base directory '{}' but it is not a valid bucket", BucketName, m_RootDir);
- m_Buckets.erase(InsertResult.first);
- return;
- }
- }
- catch (const std::exception& Err)
- {
- ZEN_WARN("creating bucket '{}' in '{}' FAILED, reason: '{}'", BucketName, BucketPath, Err.what());
- throw;
- }
+ if (CacheBucket* Bucket = GetOrCreateBucket(InBucket); Bucket != nullptr)
+ {
+ if (Bucket->Get(HashKey, OutValue))
+ {
+ TryMemCacheTrim();
+ return true;
}
}
+ return false;
+}
- ZEN_ASSERT(Bucket != nullptr);
+void
+ZenCacheDiskLayer::Put(std::string_view InBucket, const IoHash& HashKey, const ZenCacheValue& Value, std::span<IoHash> References)
+{
+ ZEN_TRACE_CPU("Z$::Disk::Put");
- Bucket->Put(HashKey, Value, References);
- TryMemCacheTrim();
+ if (CacheBucket* Bucket = GetOrCreateBucket(InBucket); Bucket != nullptr)
+ {
+ Bucket->Put(HashKey, Value, References);
+ TryMemCacheTrim();
+ }
}
void
@@ -4027,10 +4041,6 @@ ZenCacheDiskLayer::Flush()
{
RwLock::SharedLockScope __(m_Lock);
- if (m_Buckets.empty())
- {
- return;
- }
Buckets.reserve(m_Buckets.size());
for (auto& Kv : m_Buckets)
{
@@ -4061,7 +4071,6 @@ void
ZenCacheDiskLayer::ScrubStorage(ScrubContext& Ctx)
{
RwLock::SharedLockScope _(m_Lock);
-
{
std::vector<std::future<void>> Results;
Results.reserve(m_Buckets.size());
@@ -4182,20 +4191,22 @@ ZenCacheDiskLayer::EnumerateBucketContents(std::string_view
CacheValueDetails::NamespaceDetails
ZenCacheDiskLayer::GetValueDetails(const std::string_view BucketFilter, const std::string_view ValueFilter) const
{
- RwLock::SharedLockScope _(m_Lock);
CacheValueDetails::NamespaceDetails Details;
- if (BucketFilter.empty())
{
- Details.Buckets.reserve(BucketFilter.empty() ? m_Buckets.size() : 1);
- for (auto& Kv : m_Buckets)
+ RwLock::SharedLockScope IndexLock(m_Lock);
+ if (BucketFilter.empty())
+ {
+ Details.Buckets.reserve(BucketFilter.empty() ? m_Buckets.size() : 1);
+ for (auto& Kv : m_Buckets)
+ {
+ Details.Buckets[Kv.first] = Kv.second->GetValueDetails(IndexLock, ValueFilter);
+ }
+ }
+ else if (auto It = m_Buckets.find(std::string(BucketFilter)); It != m_Buckets.end())
{
- Details.Buckets[Kv.first] = Kv.second->GetValueDetails(ValueFilter);
+ Details.Buckets[It->first] = It->second->GetValueDetails(IndexLock, ValueFilter);
}
}
- else if (auto It = m_Buckets.find(std::string(BucketFilter)); It != m_Buckets.end())
- {
- Details.Buckets[It->first] = It->second->GetValueDetails(ValueFilter);
- }
return Details;
}
diff --git a/src/zenserver/cache/cachedisklayer.h b/src/zenserver/cache/cachedisklayer.h
index 11f474d5a..277371f2c 100644
--- a/src/zenserver/cache/cachedisklayer.h
+++ b/src/zenserver/cache/cachedisklayer.h
@@ -215,7 +215,7 @@ public:
uint64_t EntryCount() const;
BucketStats Stats();
- CacheValueDetails::BucketDetails GetValueDetails(const std::string_view ValueFilter) const;
+ CacheValueDetails::BucketDetails GetValueDetails(RwLock::SharedLockScope& IndexLock, const std::string_view ValueFilter) const;
void EnumerateBucketContents(std::function<void(const IoHash& Key, const CacheValueDetails::ValueDetails& Details)>& Fn) const;
void GetUsageByAccess(GcClock::TimePoint TickStart, GcClock::Duration SectionLength, std::vector<uint64_t>& InOutUsageSlots);
@@ -301,7 +301,7 @@ public:
BucketConfiguration m_Configuration;
BlockStore m_BlockStore;
Oid m_BucketId;
- std::atomic_bool m_IsFlushing{};
+ std::atomic_bool m_IsFlushing{true}; // Don't allow flush until we are properly initialized
// These files are used to manage storage of small objects for this bucket
@@ -342,7 +342,7 @@ public:
IoBuffer GetStandaloneCacheValue(ZenContentType ContentType, const IoHash& HashKey) const;
void PutInlineCacheValue(const IoHash& HashKey, const ZenCacheValue& Value, std::span<IoHash> References);
IoBuffer GetInlineCacheValue(const DiskLocation& Loc) const;
- CacheValueDetails::ValueDetails GetValueDetails(const IoHash& Key, PayloadIndex Index) const;
+ CacheValueDetails::ValueDetails GetValueDetails(RwLock::SharedLockScope&, const IoHash& Key, PayloadIndex Index) const;
void CompactReferences(RwLock::ExclusiveLockScope&);
void SetReferences(RwLock::ExclusiveLockScope&, ReferenceIndex& FirstReferenceIndex, std::span<IoHash> References);
@@ -359,20 +359,35 @@ public:
bool LockedGetReferences(ReferenceIndex FirstReferenceIndex, std::vector<IoHash>& OutReferences) const;
void ClearReferenceCache();
- void SetMetaData(BucketPayload& Payload, const ZenCacheDiskLayer::CacheBucket::BucketMetaData& MetaData);
- void RemoveMetaData(BucketPayload& Payload);
- BucketMetaData GetMetaData(const BucketPayload& Payload) const;
- void SetMemCachedData(BucketPayload& Payload, IoBuffer& MemCachedData);
- size_t RemoveMemCachedData(BucketPayload& Payload);
+ void SetMetaData(RwLock::ExclusiveLockScope&,
+ BucketPayload& Payload,
+ const ZenCacheDiskLayer::CacheBucket::BucketMetaData& MetaData);
+ void RemoveMetaData(RwLock::ExclusiveLockScope&, BucketPayload& Payload);
+ BucketMetaData GetMetaData(RwLock::SharedLockScope&, const BucketPayload& Payload) const;
+ void SetMemCachedData(RwLock::ExclusiveLockScope&, BucketPayload& Payload, IoBuffer& MemCachedData);
+ size_t RemoveMemCachedData(RwLock::ExclusiveLockScope&, BucketPayload& Payload);
- void InitializeIndexFromDisk(bool IsNew);
- uint64_t ReadIndexFile(const std::filesystem::path& IndexPath, uint32_t& OutVersion);
- uint64_t ReadLog(const std::filesystem::path& LogPath, uint64_t LogPosition);
+ void InitializeIndexFromDisk(RwLock::ExclusiveLockScope&, bool IsNew);
+ uint64_t ReadIndexFile(RwLock::ExclusiveLockScope&, const std::filesystem::path& IndexPath, uint32_t& OutVersion);
+ uint64_t ReadLog(RwLock::ExclusiveLockScope&, const std::filesystem::path& LogPath, uint64_t LogPosition);
void SaveSnapshot(const std::function<uint64_t()>& ClaimDiskReserveFunc = []() { return 0; });
- void WriteIndexSnapshot(const std::function<uint64_t()>& ClaimDiskReserveFunc = []() { return 0; });
+ void WriteIndexSnapshot(
+ RwLock::ExclusiveLockScope&,
+ const std::function<uint64_t()>& ClaimDiskReserveFunc = []() { return 0; })
+ {
+ WriteIndexSnapshotLocked(ClaimDiskReserveFunc);
+ }
+ void WriteIndexSnapshot(
+ RwLock::SharedLockScope&,
+ const std::function<uint64_t()>& ClaimDiskReserveFunc = []() { return 0; })
+ {
+ WriteIndexSnapshotLocked(ClaimDiskReserveFunc);
+ }
+ void WriteIndexSnapshotLocked(const std::function<uint64_t()>& ClaimDiskReserveFunc = []() { return 0; });
- void CompactState(std::vector<BucketPayload>& Payloads,
+ void CompactState(RwLock::ExclusiveLockScope&,
+ std::vector<BucketPayload>& Payloads,
std::vector<AccessTime>& AccessTimes,
std::vector<BucketMetaData>& MetaDatas,
std::vector<IoBuffer>& MemCachedPayloads,
@@ -406,7 +421,8 @@ public:
};
private:
- inline void TryMemCacheTrim()
+ CacheBucket* GetOrCreateBucket(std::string_view InBucket);
+ inline void TryMemCacheTrim()
{
if (m_Configuration.MemCacheTargetFootprintBytes == 0)
{
diff --git a/src/zenserver/config.cpp b/src/zenserver/config.cpp
index fe92613f4..3fe0b0c63 100644
--- a/src/zenserver/config.cpp
+++ b/src/zenserver/config.cpp
@@ -9,6 +9,7 @@
#include <zencore/except.h>
#include <zencore/fmtutils.h>
#include <zencore/iobuffer.h>
+#include <zencore/logging.h>
#include <zencore/string.h>
#include <zenhttp/zenhttp.h>
#include <zenutil/basicfile.h>
@@ -519,7 +520,6 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions)
cxxopts::value<bool>(ServerOptions.IsCleanStart)->default_value("false"));
options.add_options()("help", "Show command line help");
options.add_options()("t, test", "Enable test mode", cxxopts::value<bool>(ServerOptions.IsTest)->default_value("false"));
- options.add_options()("log-id", "Specify id for adding context to log output", cxxopts::value<std::string>(ServerOptions.LogId));
options.add_options()("data-dir", "Specify persistence root", cxxopts::value<std::string>(DataDir));
options.add_options()("snapshot-dir",
"Specify a snapshot of server state to mirror into the persistence root at startup",
@@ -528,7 +528,6 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions)
options.add_options()("powercycle",
"Exit immediately after initialization is complete",
cxxopts::value<bool>(ServerOptions.IsPowerCycle));
- options.add_options()("abslog", "Path to log file", cxxopts::value<std::string>(AbsLogFile));
options.add_options()("config", "Path to Lua config file", cxxopts::value<std::string>(ConfigFile));
options.add_options()("write-config", "Path to output Lua config file", cxxopts::value<std::string>(OutputConfigFile));
options.add_options()("no-sentry",
@@ -537,7 +536,21 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions)
options.add_options()("sentry-allow-personal-info",
"Allow personally identifiable information in sentry crash reports",
cxxopts::value<bool>(ServerOptions.SentryAllowPII)->default_value("false"));
- options.add_options()("quiet", "Disable console logging", cxxopts::value<bool>(ServerOptions.NoConsoleOutput)->default_value("false"));
+
+ // clang-format off
+ options.add_options("logging")
+ ("abslog", "Path to log file", cxxopts::value<std::string>(AbsLogFile))
+ ("log-id", "Specify id for adding context to log output", cxxopts::value<std::string>(ServerOptions.LogId))
+ ("quiet", "Disable console logging", cxxopts::value<bool>(ServerOptions.NoConsoleOutput)->default_value("false"))
+ ("log-trace", "Change selected loggers to level TRACE", cxxopts::value<std::string>(ServerOptions.Loggers[logging::level::Trace]))
+ ("log-debug", "Change selected loggers to level DEBUG", cxxopts::value<std::string>(ServerOptions.Loggers[logging::level::Debug]))
+ ("log-info", "Change selected loggers to level INFO", cxxopts::value<std::string>(ServerOptions.Loggers[logging::level::Info]))
+ ("log-warn", "Change selected loggers to level WARN", cxxopts::value<std::string>(ServerOptions.Loggers[logging::level::Warn]))
+ ("log-error", "Change selected loggers to level ERROR", cxxopts::value<std::string>(ServerOptions.Loggers[logging::level::Err]))
+ ("log-critical", "Change selected loggers to level CRITICAL", cxxopts::value<std::string>(ServerOptions.Loggers[logging::level::Critical]))
+ ("log-off", "Change selected loggers to level OFF", cxxopts::value<std::string>(ServerOptions.Loggers[logging::level::Off]))
+ ;
+ // clang-format on
options.add_option("security",
"",
@@ -952,6 +965,12 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions)
exit(0);
}
+ for (int i = 0; i < logging::level::LogLevelCount; ++i)
+ {
+ logging::ConfigureLogLevels(logging::level::LogLevel(i), ServerOptions.Loggers[i]);
+ }
+ logging::RefreshLogLevels();
+
ServerOptions.DataDir = MakeSafePath(DataDir);
ServerOptions.BaseSnapshotDir = MakeSafePath(BaseSnapshotDir);
ServerOptions.ContentDir = MakeSafePath(ContentDir);
diff --git a/src/zenserver/config.h b/src/zenserver/config.h
index 8135bf8f0..11311f9d8 100644
--- a/src/zenserver/config.h
+++ b/src/zenserver/config.h
@@ -2,6 +2,7 @@
#pragma once
+#include <zencore/logbase.h>
#include <zencore/zencore.h>
#include <zenhttp/httpserver.h>
#include <filesystem>
@@ -151,6 +152,7 @@ struct ZenServerOptions
bool SentryAllowPII = false; // Allow personally identifiable information in sentry crash reports
bool ObjectStoreEnabled = false;
bool NoConsoleOutput = false; // Control default use of stdout for diagnostics
+ std::string Loggers[zen::logging::level::LogLevelCount];
#if ZEN_WITH_TRACE
std::string TraceHost; // Host name or IP address to send trace data to
std::string TraceFile; // Path of a file to write a trace
diff --git a/src/zenserver/diag/logging.cpp b/src/zenserver/diag/logging.cpp
index e2d57b840..dc1675819 100644
--- a/src/zenserver/diag/logging.cpp
+++ b/src/zenserver/diag/logging.cpp
@@ -42,6 +42,7 @@ InitializeServerLogging(const ZenServerOptions& InOptions)
/* max files */ 16,
/* rotate on open */ true);
auto HttpLogger = std::make_shared<spdlog::logger>("http_requests", HttpSink);
+ spdlog::apply_logger_env_levels(HttpLogger);
spdlog::register_logger(HttpLogger);
// Cache request logging
@@ -53,16 +54,19 @@ InitializeServerLogging(const ZenServerOptions& InOptions)
/* max files */ 16,
/* rotate on open */ false);
auto CacheLogger = std::make_shared<spdlog::logger>("z$", CacheSink);
+ spdlog::apply_logger_env_levels(CacheLogger);
spdlog::register_logger(CacheLogger);
// Jupiter - only log upstream HTTP traffic to file
auto JupiterLogger = std::make_shared<spdlog::logger>("jupiter", FileSink);
+ spdlog::apply_logger_env_levels(JupiterLogger);
spdlog::register_logger(JupiterLogger);
// Zen - only log upstream HTTP traffic to file
auto ZenClientLogger = std::make_shared<spdlog::logger>("zenclient", FileSink);
+ spdlog::apply_logger_env_levels(ZenClientLogger);
spdlog::register_logger(ZenClientLogger);
FinishInitializeLogging(LogOptions);
diff --git a/src/zenserver/projectstore/httpprojectstore.cpp b/src/zenserver/projectstore/httpprojectstore.cpp
index 261485834..0ba49cf8a 100644
--- a/src/zenserver/projectstore/httpprojectstore.cpp
+++ b/src/zenserver/projectstore/httpprojectstore.cpp
@@ -276,6 +276,11 @@ HttpProjectService::HttpProjectService(CidStore& Store, ProjectStore* Projects,
HttpVerb::kGet);
m_Router.RegisterRoute(
+ "{project}/oplog/{log}/chunkinfos",
+ [this](HttpRouterRequest& Req) { HandleChunkInfosRequest(Req); },
+ HttpVerb::kGet);
+
+ m_Router.RegisterRoute(
"{project}/oplog/{log}/{chunk}/info",
[this](HttpRouterRequest& Req) { HandleChunkInfoRequest(Req); },
HttpVerb::kGet);
@@ -643,6 +648,41 @@ HttpProjectService::HandleFilesRequest(HttpRouterRequest& Req)
}
void
+HttpProjectService::HandleChunkInfosRequest(HttpRouterRequest& Req)
+{
+ ZEN_TRACE_CPU("ProjectService::ChunkInfos");
+
+ HttpServerRequest& HttpReq = Req.ServerRequest();
+
+ const auto& ProjectId = Req.GetCapture(1);
+ const auto& OplogId = Req.GetCapture(2);
+
+ CbObject ResponsePayload;
+ std::pair<HttpResponseCode, std::string> Result = m_ProjectStore->GetProjectChunkInfos(ProjectId, OplogId, ResponsePayload);
+ if (Result.first == HttpResponseCode::OK)
+ {
+ return HttpReq.WriteResponse(HttpResponseCode::OK, ResponsePayload);
+ }
+ else
+ {
+ if (Result.first == HttpResponseCode::BadRequest)
+ {
+ m_ProjectStats.BadRequestCount++;
+ }
+ ZEN_DEBUG("Request {}: '{}' failed with {}. Reason: `{}`",
+ ToString(HttpReq.RequestVerb()),
+ HttpReq.QueryString(),
+ static_cast<int>(Result.first),
+ Result.second);
+ }
+ if (Result.second.empty())
+ {
+ return HttpReq.WriteResponse(Result.first);
+ }
+ return HttpReq.WriteResponse(Result.first, HttpContentType::kText, Result.second);
+}
+
+void
HttpProjectService::HandleChunkInfoRequest(HttpRouterRequest& Req)
{
ZEN_TRACE_CPU("ProjectService::ChunkInfo");
diff --git a/src/zenserver/projectstore/httpprojectstore.h b/src/zenserver/projectstore/httpprojectstore.h
index 9998ae83e..9990ee264 100644
--- a/src/zenserver/projectstore/httpprojectstore.h
+++ b/src/zenserver/projectstore/httpprojectstore.h
@@ -64,6 +64,7 @@ private:
void HandleProjectListRequest(HttpRouterRequest& Req);
void HandleChunkBatchRequest(HttpRouterRequest& Req);
void HandleFilesRequest(HttpRouterRequest& Req);
+ void HandleChunkInfosRequest(HttpRouterRequest& Req);
void HandleChunkInfoRequest(HttpRouterRequest& Req);
void HandleChunkByIdRequest(HttpRouterRequest& Req);
void HandleChunkByCidRequest(HttpRouterRequest& Req);
diff --git a/src/zenserver/projectstore/projectstore.cpp b/src/zenserver/projectstore/projectstore.cpp
index c6097dea2..9ba8e3a19 100644
--- a/src/zenserver/projectstore/projectstore.cpp
+++ b/src/zenserver/projectstore/projectstore.cpp
@@ -752,6 +752,21 @@ ProjectStore::Oplog::GetAllChunksInfo()
}
void
+ProjectStore::Oplog::IterateChunkMap(std::function<void(const Oid&, const IoHash&)>&& Fn)
+{
+ RwLock::SharedLockScope _(m_OplogLock);
+ if (!m_Storage)
+ {
+ return;
+ }
+
+ for (const auto& Kv : m_ChunkMap)
+ {
+ Fn(Kv.first, Kv.second);
+ }
+}
+
+void
ProjectStore::Oplog::IterateFileMap(
std::function<void(const Oid&, const std::string_view& ServerPath, const std::string_view& ClientPath)>&& Fn)
{
@@ -2215,9 +2230,9 @@ ProjectStore::GetProjectFiles(const std::string_view ProjectId, const std::strin
}
std::pair<HttpResponseCode, std::string>
-ProjectStore::GetProjectChunks(const std::string_view ProjectId, const std::string_view OplogId, CbObject& OutPayload)
+ProjectStore::GetProjectChunkInfos(const std::string_view ProjectId, const std::string_view OplogId, CbObject& OutPayload)
{
- ZEN_TRACE_CPU("ProjectStore::GetProjectChunks");
+ ZEN_TRACE_CPU("ProjectStore::GetProjectChunkInfos");
using namespace std::literals;
@@ -2235,21 +2250,22 @@ ProjectStore::GetProjectChunks(const std::string_view ProjectId, const std::stri
}
Project->TouchOplog(OplogId);
- std::vector<ProjectStore::Oplog::ChunkInfo> ChunkInfo = FoundLog->GetAllChunksInfo();
+ std::vector<std::pair<Oid, IoHash>> ChunkInfos;
+ FoundLog->IterateChunkMap([&ChunkInfos](const Oid& Id, const IoHash& Hash) { ChunkInfos.push_back({Id, Hash}); });
CbObjectWriter Response;
+ Response.BeginArray("chunkinfos"sv);
- Response.BeginArray("chunks"sv);
- for (ProjectStore::Oplog::ChunkInfo& Info : ChunkInfo)
+ for (const auto& ChunkInfo : ChunkInfos)
{
- Response << Info.ChunkId;
- }
- Response.EndArray();
-
- Response.BeginArray("sizes"sv);
- for (ProjectStore::Oplog::ChunkInfo& Info : ChunkInfo)
- {
- Response << Info.ChunkSize;
+ if (IoBuffer Chunk = FoundLog->FindChunk(ChunkInfo.first))
+ {
+ Response.BeginObject();
+ Response << "id"sv << ChunkInfo.first;
+ Response << "rawhash"sv << ChunkInfo.second;
+ Response << "rawsize"sv << Chunk.GetSize();
+ Response.EndObject();
+ }
}
Response.EndArray();
diff --git a/src/zenserver/projectstore/projectstore.h b/src/zenserver/projectstore/projectstore.h
index 555f8bdf2..57cda8ae7 100644
--- a/src/zenserver/projectstore/projectstore.h
+++ b/src/zenserver/projectstore/projectstore.h
@@ -93,6 +93,7 @@ public:
};
std::vector<ChunkInfo> GetAllChunksInfo();
+ void IterateChunkMap(std::function<void(const Oid&, const IoHash& Hash)>&& Fn);
void IterateFileMap(std::function<void(const Oid&, const std::string_view& ServerPath, const std::string_view& ClientPath)>&& Fn);
void IterateOplog(std::function<void(CbObjectView)>&& Fn);
void IterateOplogWithKey(std::function<void(int, const Oid&, CbObjectView)>&& Fn);
@@ -306,9 +307,9 @@ public:
const std::string_view OplogId,
bool FilterClient,
CbObject& OutPayload);
- std::pair<HttpResponseCode, std::string> GetProjectChunks(const std::string_view ProjectId,
- const std::string_view OplogId,
- CbObject& OutPayload);
+ std::pair<HttpResponseCode, std::string> GetProjectChunkInfos(const std::string_view ProjectId,
+ const std::string_view OplogId,
+ CbObject& OutPayload);
std::pair<HttpResponseCode, std::string> GetChunkInfo(const std::string_view ProjectId,
const std::string_view OplogId,
const std::string_view ChunkId,
diff --git a/src/zenstore/compactcas.cpp b/src/zenstore/compactcas.cpp
index 42302c4a9..96ab65a5f 100644
--- a/src/zenstore/compactcas.cpp
+++ b/src/zenstore/compactcas.cpp
@@ -989,13 +989,14 @@ CasContainerStrategy::ReadIndexFile(const std::filesystem::path& IndexPath, uint
Entries.resize(128 * 1024 / sizeof(CasDiskIndexEntry));
uint64_t RemainingEntries = Header.EntryCount;
+ uint64_t ReadOffset = sizeof(CasDiskIndexHeader);
do
{
const uint64_t NumToRead = Min(RemainingEntries, Entries.size());
Entries.resize(NumToRead);
- ObjectIndexFile.Read(Entries.data(), Entries.size() * sizeof(CasDiskIndexEntry), sizeof(CasDiskIndexHeader));
+ ObjectIndexFile.Read(Entries.data(), Entries.size() * sizeof(CasDiskIndexEntry), ReadOffset);
std::string InvalidEntryReason;
for (const CasDiskIndexEntry& Entry : Entries)
@@ -1011,6 +1012,7 @@ CasContainerStrategy::ReadIndexFile(const std::filesystem::path& IndexPath, uint
}
RemainingEntries -= NumToRead;
+ ReadOffset += NumToRead * sizeof(CasDiskIndexEntry);
} while (RemainingEntries);
OutVersion = CasDiskIndexHeader::CurrentVersion;
diff --git a/src/zenutil/logging.cpp b/src/zenutil/logging.cpp
index d0a6ac0b4..d82789e42 100644
--- a/src/zenutil/logging.cpp
+++ b/src/zenutil/logging.cpp
@@ -12,6 +12,7 @@ ZEN_THIRD_PARTY_INCLUDES_END
#include <zencore/compactbinary.h>
#include <zencore/filesystem.h>
+#include <zencore/logging.h>
#include <zencore/string.h>
#include <zenutil/logging/fullformatter.h>
#include <zenutil/logging/jsonformatter.h>
@@ -152,21 +153,21 @@ BeginInitializeLogging(const LoggingOptions& LogOptions)
void
FinishInitializeLogging(const LoggingOptions& LogOptions)
{
- spdlog::level::level_enum LogLevel = spdlog::level::info;
+ logging::level::LogLevel LogLevel = logging::level::Info;
if (LogOptions.IsDebug)
{
- LogLevel = spdlog::level::debug;
+ LogLevel = logging::level::Debug;
}
if (LogOptions.IsTest)
{
- LogLevel = spdlog::level::trace;
+ LogLevel = logging::level::Trace;
}
// Configure all registered loggers according to settings
- spdlog::set_level(LogLevel);
+ logging::RefreshLogLevels(LogLevel);
spdlog::flush_on(spdlog::level::err);
spdlog::flush_every(std::chrono::seconds{2});
spdlog::set_formatter(std::make_unique<logging::full_formatter>(LogOptions.LogId, std::chrono::system_clock::now()));