@Override
  public @NotNull DurableMapOverAppendOnlyLog<K, V> open(@NotNull Path storagePath) throws IOException {
    String name = storagePath.getFileName().toString();
    Path mapPath = storagePath.resolveSibling(name + MAP_FILE_SUFFIX);
    boolean mapFileExists = Files.exists(mapPath);

    return logFactory.wrapStorageSafely(
      storagePath,
      entriesLog -> {
        try {
          return mapFactory.wrapStorageSafely(
            mapPath,
            keyHashToEntryOffsetMap -> {
              if (!entriesLog.isEmpty() && keyHashToEntryOffsetMap.isEmpty()) {
                if (keyHashToEntryOffsetMap instanceof NonDurableNonParallelIntToMultiIntMap) {
                  //empty, non-durable map: must be filled from the log
                  fillValueHashToIdMap(entriesLog, keyEquality, entryExternalizer, keyHashToEntryOffsetMap);
                  LOG.info("[" + name + "]: keyToOffsetMap (in memory) was filled from entriesLog " +
                           "(" + keyHashToEntryOffsetMap.size() + " records)");
                }
                else if (!mapFileExists) {
                  //empty, durable map, AND map file didn't exists AND entriesLog is not empty
                  //  => we probably lost the .map file, and it was created fresh => try to fill the map from
                  //  the entriesLog also:
                  fillValueHashToIdMap(entriesLog, keyEquality, entryExternalizer, keyHashToEntryOffsetMap);
                  LOG.info("[" + name + "]: keyToOffsetMap (in memory) was filled from entriesLog " +
                           "(" + keyHashToEntryOffsetMap.size() + " records)");
                }
                //else (empty, durable map, not freshly created): valid case if all entries were removed
              }

              return new DurableMapOverAppendOnlyLog<>(
                entriesLog,
                keyHashToEntryOffsetMap,
                keyEquality,
                valueEquality,
                entryExternalizer
              );
            }
          );
        }
        catch (CorruptedException e) {
          if (!rebuildMapFromLogIfInconsistent) {
            throw e;
          }

          //If keyToOffsetMap is durable, and corrupted -> below we try rebuilding such a map
          //   (i.e. it is a 'recovery' for durable maps corrupted by improper app termination)

          //TODO RC: this branch is a bit of hack: CorruptedException could be thrown not only from the mapFactory, but also
          //         from the entriesLog -- there is no way to find out the source.
          //         Would be better to rely on the mapFactory itself to DROP_AND_CREATE_EMPTY_MAP -- but we need a way to know
          //         map _was_ re-created from 0. Currently there is no way to know that: mapFactory just provides a map,
          //         which is empty -- maybe because it is genuinely empty, maybe because it was re-created from 0.
          //         The recovery logic may be simpler & more reliable if e.g. StorageFactory provides an optional additional
          //         arg (something like OpeningConditions) with details about how exactly the storage was opened -- e.g. with
          //         nuances or not.

          FileUtil.delete(mapPath);//assume MapFactory has closed & unmapped the map before throwing exception

          return mapFactory.wrapStorageSafely(
            mapPath,
            keyHashToEntryOffsetMap -> {
              if (!entriesLog.isEmpty()) {
                LOG.warn("[" + name + "]: .keyToOffsetMap map corrupted -> try recovering the map from entriesLog " +
                         "(impl: " + keyHashToEntryOffsetMap.getClass() + ")", e);


                fillValueHashToIdMap(entriesLog, keyEquality, entryExternalizer, keyHashToEntryOffsetMap);

                LOG.info("[" + name + "]: keyToOffsetMap (durable) was recovered from entriesLog " +
                         "(" + keyHashToEntryOffsetMap.size() + " records)");
              }

              return new DurableMapOverAppendOnlyLog<>(
                entriesLog,
                keyHashToEntryOffsetMap,
                keyEquality,
                valueEquality,
                entryExternalizer
              );
            }
          );

          //TODO RC: what if entriesLog was recovered? -- could it be the keyHashToEntryOffsetMap map is somehow .wasClosedProperly,
          //         but still is inconsistent with valuesLog? It seems it could: current implementation of append-only-log
          //         _could_ sometimes lose the written-and-commited record: i.e. one of the previous values in the log
          //         wasn't committed, and even record header wasn't written -- and because of that whole region after
          //         that allocated-but-not-yet-started record is lost (see more detailed discussion in the implementation
          //         class). So there _is_ a chance that value is appended to the log, and valueId is inserted into the map,
          //         but value record is lost on crash, so the id put in the map is now invalid.
          //         So we should either modify AppendOnlyLog so that it doesn't allow that (e.g. we could not return
          //         from allocateRecord until at least header is written -- again, see discussion in the impl class) -- or
          //         we should always rebuild the map if AppendOnlyLog was recovered, _and_ the recovered region >0 -- even
          //         if the map itself wasClosedProperly=true.

          //MAYBE separate 'always rebuild map' and 'rebuild map if inconsistent'
          //      (both requires .open(path,CREATE_NEW) method)
        }
      }
    );
  }
@Override
  public @NotNull DurableMapOverAppendOnlyLog<K, V> open(@NotNull Path storagePath)