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

  1. Dispose every LightEpoch instance when done so its slot is released back to the static tracker.
  2. In tests, call LightEpoch.ResetAllInstances() between test cases to reclaim leaked slots.
  3. 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

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


AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13). Data as JSON: /api/errors/3261c4eda04bb833. Report an issue: GitHub.