public void initializeStorages() throws IOException {
    if (enableVfsLog) {
      vfsLog = VfsLogImpl.open(vfsLogDir, false, true);
    }
    else {
      vfsLog = null;
    }

    CompletableFuture<ScannableDataEnumeratorEx<String>> namesStorageFuture =
      executorService.async(() -> createFileNamesEnumerator(namesFile));
    CompletableFuture<VFSAttributesStorage> attributesStorageFuture =
      executorService.async(() -> createAttributesStorage(attributesFile));
    CompletableFuture<VFSContentStorage> contentsStorageFuture =
      executorService.async(() -> createContentStorage(contentsHashesFile, contentsFile));
    CompletableFuture<PersistentFSRecordsStorage> recordsStorageFuture = executorService.async(() -> createRecordsStorage(recordsFile));

    //TODO RC: if !REUSE_DELETED_FILE_IDS -> check recordsStorage.maxAllocatedID() -> rebuild VFS if maxID >~ MAX_INT/2
    if (FSRecordsImpl.REUSE_DELETED_FILE_IDS) {
      //Initiate async scanning of the recordsStorage to fill reusableFileIds, and create lazy-accessor for it.
      collectDeletedFileRecordsTask = executorService.async(() -> {
        //TODO RC: limit max number of reusable fileIds -- i.e. what if there are millions of them?
        //         5-10_000 max should be enough.
        IntList reusableFileIds = new IntArrayList(1024);
        //fill up reusable (=deleted) records:
        PersistentFSRecordsStorage storage = recordsStorageFuture.join();
        storage.processAllRecords((fileId, nameId, flags, parentId, attributeRecordId, contentId, corrupted) -> {
          if (hasDeletedFlag(flags)) {
            reusableFileIds.add(fileId);
          }
        });
        LOG.info("VFS scanned: " + reusableFileIds.size() + " deleted files to reuse");
        return reusableFileIds;
      });
    }
    else {
      collectDeletedFileRecordsTask = CompletableFuture.completedFuture(new IntArrayList(0));
    }
    //RC: we don't need volatile/atomicLazy, since computation is idempotent: same instance returned always.
    //    So _there could be_ a data race, but it is a benign race.
    reusableFileIdsLazy = NotNullLazyValue.lazy(() -> {
      try {
        return collectDeletedFileRecordsTask.join();
      }
      catch (Throwable e) {
        throw new IllegalStateException("Lazy reusableFileIds computation is failed", e);
      }
    });

    ExceptionUtil.runAllAndRethrowAllExceptions(
      ASYNC_EXCEPTIONS_REPORTER,
      () -> {
        //MAYBE RC: I'd like to have completely new Enumerator here:
        //          1. Without legacy issues with null vs 'null' strings
        //          2. With explicit 'id' stored in a file (instead of implicit id=row num)
        attributesEnumerator = new SimpleStringPersistentEnumerator(enumeratedAttributesFile);
      },
      () -> {
        recordsStorage = recordsStorageFuture.join();
      },
      () -> {
        namesStorage = namesStorageFuture.join();
      },
      () -> {
        attributesStorage = attributesStorageFuture.join();
      },
      () -> {
        contentsStorage = contentsStorageFuture.join();
      }
    );
  }
public void initializeStorages()