Skip to content

[Java] Unsafe lazy initialization of registry singletons can lose registrations at startup #16

Open

Description

@mpc00620

Problem

Several lazy singletons check a non-volatile field outside the lock and do not re-check it inside:

  • factory/ExtractorRegistryFactory.java:30-38
  • factory/TransformerRegistryFactory.java (same pattern)
  • factory/LocalCacheRegistryFactory.java:29-40
  • factory/ExperimentManagerFactory.java (around lines 41, 54 and 66)

Two threads calling getSingleton() concurrently can each build an instance, and the second silently replaces the first. A component that captured the first instance (for example the evaluator) then reads a registry that the registration task never fills, so evaluations fail (fail-open) until restart.

In addition, the extractor and transformer registries are backed by plain HashMaps (ExtractorRegistryFactory.java:35, TransformerRegistryFactory.java:35). The one-shot setup task fills them on a scheduler thread while request threads read them, with no happens-before edge.

AWSServiceClientFactory already does this correctly (volatile plus a re-check) and is not affected.

Proposed fix

  • Use a volatile field with a re-check inside the lock (or the holder idiom) for each singleton.
  • Back the registries with ConcurrentHashMap.
  • No API change.

Test

A concurrent first-call test asserting that every caller gets the same instance. Races are inherently probabilistic, so this test may not fail on every run before the fix. The fix is verifiable by inspection.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions