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);
}