Skip to content

DataStoreManager: getDatastore reads dataMap without the lock register holds, so a concurrent read can return null #515

Description

@wisdommen

DataStoreManager.getDatastore reads an unsynchronised HashMap that register mutates under a lock, so a concurrent read can return null for a type that is registered.

What was observed

DataStoreManagerTest$ConcurrentReadWriteTests.concurrentReadWriteShouldBeSafe failed twice locally during Phase 17 wave-0 work, both times while another Maven build shared the host, and passed on every re-run and in CI. The failing assertion is the one that exists for exactly this:

[a concurrent read of an already-registered type must never return null]

init-type-0 is registered before any thread starts, and one of the twenty concurrent readers still got null back for it.

Why it is not only a flaky test

In manager/DataStoreManager:

  • dataMap is a plain java.util.HashMap;
  • register(DataStore) and unregister(DataStore) are static synchronized and mutate it;
  • getDatastore(String) is not synchronised and reads it — three times per call, on the "json" fallback path.

A read that runs while a writer is rehashing that map is unsynchronised access to a structure being restructured. Returning null for a key that is present is the mildest outcome of that race; the reader also has no happens-before edge to the writer's put, so a newly registered store can be invisible to it indefinitely. The test is doing its job — it catches a real race when the machine is loaded enough to widen the window.

Suggested direction (not prescriptive)

Either make dataMap a ConcurrentHashMap and drop the synchronized on the writers, or synchronise the reader on the same monitor. The first keeps reads lock-free; the second is a two-line change. Whichever is chosen, getDatastore's three reads of the map should become one lookup so the "json" fallback cannot observe two different states of the map.

Scope

Not fixed in the pull request that found it — that one is scoped to /upm uninstall (#503, #501) and does not touch this class, whose last change was in phase 8. Filed for wave 4.

Reproducing

Run the framework test suite while the machine is loaded (a second Maven build is enough):

mvn -B clean verify

The failure is intermittent; several consecutive runs on an idle machine pass.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:data数据持久化、事务、配置bugSomething isn't workingeffort:S一天以内

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions