Skip to content

An empty domain map is left behind by a replica DB creation which bails out #818

Description

@vharseko

FileChangelogDB.getOrCreateReplicaDB() inserts a domain map, and announces the domain to every
registered multi domain cursor, before it can know whether it will create anything in it:

// FileChangelogDB.java:229-241
final ConcurrentMap<Integer, FileReplicaDB> newValue = new ConcurrentHashMap<>();
final ConcurrentMap<Integer, FileReplicaDB> previousValue = domainToReplicaDBs.putIfAbsent(baseDN, newValue);
...
// we just created a new domain => update all cursors
for (MultiDomainDBCursor cursor : registeredMultiDomainCursors)
{
  cursor.addDomain(baseDN, null);
}

The caller can then bail out of the creation without ever putting a replica DB in that map:
today when the domain map was concurrently removed (:266), and, once #813 is fixed, when a
shutdown was initiated. The empty map stays in domainToReplicaDBs for the lifetime of the
changelog. On the shutdown path it outlives shutdownDB() altogether, which drained
domainToReplicaDBs before that map was inserted into it.

What that empty domain map is then worth:

  • it is a key of domainToReplicaDBs, so getCursorFrom() adds it to every multi domain
    cursor created afterwards (:697-704), i.e. the external changelog walks a domain which has
    no replica DB at all;
  • getDomainMap(), getDomainOldestCSNs() and getDomainNewestCSNs() answer for it as they
    would for an unknown domain, so those are unaffected;
  • clearDB() iterates the keys and calls removeDomain() on it (:427-430), which reaches
    replicationEnv.clearGenerationId() for a domain the changelog holds nothing for — and, after
    a shutdown, on an environment which is closed.

Proposed fix

Drop the map the caller inserted when the creation it was inserted for does not happen:

// FileChangelogDB.java:266
if (domainToReplicaDBs.get(baseDN) != domainMap)
{
  return null;
}

becomes, together with the shutdown check of #813, a bail-out which first does

domainToReplicaDBs.remove(baseDN, domainMap);

The conditional remove is safe under the monitor of domainMap: a shutdown which had already
seen that map only finds the entry gone when it acquires the monitor, and Iterator.remove()
on an entry which is no longer mapped is a no-op.

Found while analysing #813.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions