How-To: Diagnose Pool Problems

Goal: answer the question every pooling system provokes - is pooling causing this? - in one command, then find out which of the usual causes it is.

Prerequisites
A pool in use and something misbehaving.

1. Rule pooling in or out

text
Crimson.ObjectPool.Bypass 1

Every acquire becomes a real SpawnActor and every release a real Destroy. Pooling is entirely out of the way.

ResultConclusion
Bug persistsNot a pooling bug. Stop looking here.
Bug disappearsA reuse bug. Go to step 2.
Verify
With bypass on, gameplay should be identical apart from the actor churn. If behaviour changes in some other way, that difference is itself the clue.

2. Check the three reuse rules

A bug that disappears under bypass is almost always one of these. Full detail in Concept: The Reuse Contract.

SymptomLikely cause
Works the first time, wrong every time afterPer-use state initialised in BeginPlay, which runs once per actor lifetime.
Second use ends instantlyA one-way latch (bHasFired, bFinished) never reset on acquire.
Effects or damage happen two, three, four times overA delegate bound in BeginPlay and rebound on every acquire.
Actor is visible but collides with nothingThe actor-level collision flag was cleared on release and never re-enabled.

3. Read what the pools are doing

text
Crimson.ObjectPool.Dump
ReadingMeaning
created keeps rising during steady playInstances are acquired and never released. The pool is a leak, not a pool.
spawnsAvoided stays at 0Nothing is ever being recycled - same cause as above.
highWater at or above capacityUndersized. It is quietly spawning past its budget on every burst.
live never returns to 0 when idleSomething is holding instances it should have released.
Verify
Run the scenario, return to idle, and dump again. A healthy pool sits at live=0 with pooled equal to its peak usage.

4. Multiplayer symptoms

SymptomLikely cause
Clients lose the actor a few seconds after each releaseNet dormancy disabled, or the actor is being moved far away on release.
Clients act on the previous use's dataCOND_InitialOnly - it is never resent on a reused channel.
Rapid reuse is skipped on clientsByte-identical activation state. Add a monotonic counter.
Warning: refused on a clientClient code is calling acquire or release. Both are server-only for replicated classes.

All four are covered in How-To: Pool a Replicated Actor.

See also

  • Concept: The Reuse Contract
  • How-To: Pool a Replicated Actor
  • How-To: Size and Prewarm a Pool