private void restoreFromIncrementalBackup()

in core/src/main/java/com/jetbrains/youtrackdb/internal/core/storage/disk/DiskStorage.java [1762:1969]


  private void restoreFromIncrementalBackup(
      final String charset,
      final Locale serverLocale,
      final Locale locale,
      final ContextConfiguration contextConfiguration,
      final byte[] aesKey,
      final InputStream inputStream,
      final boolean isFull)
      throws IOException {
    final List<String> currentFiles = new ArrayList<>(writeCache.files().keySet());

    final var bufferedInputStream = new BufferedInputStream(inputStream);
    final var zipInputStream =
        new ZipInputStream(bufferedInputStream, Charset.forName(charset));
    final var pageSize = writeCache.pageSize();

    ZipEntry zipEntry;
    LogSequenceNumber maxLsn = null;

    List<String> processedFiles = new ArrayList<>();

    if (isFull) {
      final var files = writeCache.files();
      for (var entry : files.entrySet()) {
        final var fileId = writeCache.fileIdByName(entry.getKey());

        assert entry.getValue().equals(fileId);
        readCache.deleteFile(fileId, writeCache);
      }
    }

    final var walTempDir = createWalTempDirectory();

    byte[] encryptionIv = null;
    byte[] walIv = null;

    entryLoop:
    while ((zipEntry = zipInputStream.getNextEntry()) != null) {
      switch (zipEntry.getName()) {
        case IV_NAME -> {
          walIv = restoreIv(zipInputStream);
          continue;
        }
        case ENCRYPTION_IV -> {
          encryptionIv = restoreEncryptionIv(zipInputStream);
          continue;
        }
        case CONF_ENTRY_NAME -> {
          replaceConfiguration(zipInputStream);

          continue;
        }
      }

      if (zipEntry.getName().equalsIgnoreCase("database_instance.uuid")) {
        continue;
      }

      if (zipEntry.getName().equals(CONF_UTF_8_ENTRY_NAME)) {
        replaceConfiguration(zipInputStream);

        continue;
      }

      if (zipEntry
          .getName()
          .toLowerCase(serverLocale)
          .endsWith(CASDiskWriteAheadLog.WAL_SEGMENT_EXTENSION)) {
        final var walName = zipEntry.getName();
        final var segmentIndex =
            walName.lastIndexOf(
                '.', walName.length() - CASDiskWriteAheadLog.WAL_SEGMENT_EXTENSION.length() - 1);
        if (segmentIndex < 0) {
          throw new IllegalStateException("Can not find index of WAL segment");
        }

        addFileToDirectory(
            contextConfiguration.getValueAsString(ContextConfiguration.WAL_BASE_NAME,
                ContextConfiguration.WAL_DEFAULT_NAME) + walName.substring(segmentIndex),
            zipInputStream, walTempDir);
        continue;
      }

      if (aesKey != null && encryptionIv == null) {
        throw new SecurityException(name, "IV can not be null if encryption key is provided");
      }

      final var binaryFileId = new byte[LongSerializer.LONG_SIZE];
      IOUtils.readFully(zipInputStream, binaryFileId, 0, binaryFileId.length);

      final var expectedFileId = LongSerializer.deserializeLiteral(binaryFileId, 0);
      long fileId;

      var rootDirectory = storagePath;
      var zipEntryPath = rootDirectory.resolve(zipEntry.getName()).normalize();

      if (!zipEntryPath.startsWith(rootDirectory)) {
        throw new IllegalStateException("Bad zip entry " + zipEntry.getName());
      }
      if (!zipEntryPath.getParent().equals(rootDirectory)) {
        throw new IllegalStateException("Bad zip entry " + zipEntry.getName());
      }

      var fileName = zipEntryPath.getFileName().toString();
      if (!writeCache.exists(fileName)) {
        fileId = readCache.addFile(fileName, expectedFileId, writeCache);
      } else {
        fileId = writeCache.fileIdByName(fileName);
      }

      if (!writeCache.fileIdsAreEqual(expectedFileId, fileId)) {
        throw new StorageException(name,
            "Can not restore database from backup because expected and actual file ids are not the"
                + " same");
      }

      while (true) {
        final var data = new byte[pageSize + LongSerializer.LONG_SIZE];

        var rb = 0;

        while (rb < data.length) {
          final var b = zipInputStream.read(data, rb, data.length - rb);

          if (b == -1) {
            if (rb > 0) {
              throw new StorageException(name, "Can not read data from file " + fileName);
            } else {
              processedFiles.add(fileName);
              continue entryLoop;
            }
          }

          rb += b;
        }

        final var pageIndex = LongSerializer.INSTANCE.deserializeNative(data, 0);

        if (aesKey != null) {
          doEncryptionDecryption(
              Cipher.DECRYPT_MODE, aesKey, expectedFileId, pageIndex, data, encryptionIv);
        }

        var cacheEntry = readCache.loadForWrite(fileId, pageIndex, writeCache, true, null);

        if (cacheEntry == null) {
          do {
            if (cacheEntry != null) {
              readCache.releaseFromWrite(cacheEntry, writeCache, true);
            }

            cacheEntry = readCache.allocateNewPage(fileId, writeCache, null);
          } while (cacheEntry.getPageIndex() != pageIndex);
        }

        try {
          final var buffer = cacheEntry.getCachePointer().getBuffer();
          assert buffer != null;
          final var backedUpPageLsn =
              DurablePage.getLogSequenceNumber(LongSerializer.LONG_SIZE, data);
          if (isFull) {
            buffer.put(0, data, LongSerializer.LONG_SIZE, data.length - LongSerializer.LONG_SIZE);

            if (maxLsn == null || maxLsn.compareTo(backedUpPageLsn) < 0) {
              maxLsn = backedUpPageLsn;
            }
          } else {
            final var currentPageLsn =
                DurablePage.getLogSequenceNumberFromPage(buffer);
            if (backedUpPageLsn.compareTo(currentPageLsn) > 0) {
              buffer.put(
                  0, data, LongSerializer.LONG_SIZE, data.length - LongSerializer.LONG_SIZE);

              if (maxLsn == null || maxLsn.compareTo(backedUpPageLsn) < 0) {
                maxLsn = backedUpPageLsn;
              }
            }
          }

        } finally {
          readCache.releaseFromWrite(cacheEntry, writeCache, true);
        }
      }
    }

    currentFiles.removeAll(processedFiles);

    for (var file : currentFiles) {
      if (writeCache.exists(file)) {
        final var fileId = writeCache.fileIdByName(file);
        readCache.deleteFile(fileId, writeCache);
      }
    }

    try (final var restoreLog =
        createWalFromIBUFiles(walTempDir, contextConfiguration, locale, walIv)) {
      if (restoreLog != null) {
        final var beginLsn = restoreLog.begin();
        restoreFrom(restoreLog, beginLsn);
      }
    }

    if (maxLsn != null && writeAheadLog != null) {
      writeAheadLog.moveLsnAfter(maxLsn);
    }

    FileUtils.deleteRecursively(walTempDir);
  }