microsoft/garnet · error · InvalidOperationException
Exceeded maximum number of active LightEpoch instances {Acti
Error message
Exceeded maximum number of active LightEpoch instances {ActiveInstanceCount()} {InstanceIndexBuffer.MaxInstances} What it means
LightEpoch uses a static InstanceTracker of fixed size (MaxInstances = 1024) to assign each LightEpoch instance a unique slot. SelectInstance scans all 1024 slots for one marked kInvalidIndex and claims it; if none is free it throws InvalidOperationException. This caps the total number of simultaneously-live LightEpoch instances process-wide.
Source
Thrown at libs/client/LightEpoch.cs:197
CurrentEpoch = 1;
SafeToReclaimEpoch = 0;
// Mark all epoch table entries as "available"
for (int i = 0; i < kDrainListSize; i++)
drainList[i].epoch = long.MaxValue;
drainCount = 0;
}
int SelectInstance()
{
for (var i = 0; i < InstanceIndexBuffer.MaxInstances; i++)
{
ref var entry = ref InstanceTracker.GetRef(i);
// Try to claim this instance ID (indicated as 1 in the entry)
if (kInvalidIndex == Interlocked.CompareExchange(ref entry, 1, kInvalidIndex))
return i;
}
throw new InvalidOperationException($"Exceeded maximum number of active LightEpoch instances {ActiveInstanceCount()} {InstanceIndexBuffer.MaxInstances}");
}
/// <summary>
/// Number of active LightEpoch instances. Used for testing and diagnostics.
/// </summary>
/// <returns></returns>
public static int ActiveInstanceCount()
{
int count = 0;
for (var i = 0; i < InstanceIndexBuffer.MaxInstances; i++)
{
if (kInvalidIndex != InstanceTracker.GetRef(i))
count++;
}
return count;
}
/// <summary>View on GitHub (pinned to 951b0fc683)
Solutions
- Dispose every LightEpoch instance when done so its slot is released back to the static tracker.
- In tests, call LightEpoch.ResetAllInstances() between test cases to reclaim leaked slots.
- Audit for LightEpoch leaks — the count should stabilize, not grow, over the process lifetime.
Example fix
// before — epoch leaked each iteration
foreach (var cfg in configs)
{
var epoch = new LightEpoch();
epoch.Initialize(...);
// ... never disposed ...
}
// after — dispose to free the instance slot
foreach (var cfg in configs)
using (var epoch = new LightEpoch())
{
epoch.Initialize(...);
// ...
} Defensive patterns
Strategy: validation
Validate before calling
// Ensure you are not exceeding the process-wide instance cap before creating more
if (LightEpoch.ActiveInstanceCount() >= 1024)
throw new InvalidOperationException("too many live LightEpoch instances; dispose some first"); Type guard
// Use ActiveInstanceCount() to check headroom before constructing a new epoch
if (LightEpoch.ActiveInstanceCount() < 1024) { /* safe to create */ } Try / catch
try { var epoch = new LightEpoch(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("maximum number of active LightEpoch")) { /* dispose leaked instances and retry */ } Prevention
- Dispose every LightEpoch when done to release its instance slot.
- Call LightEpoch.ResetAllInstances() between test runs to reclaim leaked slots.
- Monitor ActiveInstanceCount() for monotonic growth indicating a leak.
When it happens
Trigger: Constructing more than 1024 LightEpoch instances in a single process without disposing prior ones; a test harness that creates an epoch per iteration without calling ResetAllInstances() or Dispose().
Common situations: Unit tests spinning up many Garnet stores/epochs in a loop; a long-running process leaking LightEpoch instances by forgetting Dispose; integration tests that do not reset static state between runs.
Related errors
- LightEpoch
- Getting handle in disposed device
- Throttle count is negative
- Async operations not supported over protected epoch
- Getting handle in disposed device
AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13).
Data as JSON: /api/errors/3261c4eda04bb833.
Report an issue: GitHub.