Fix for memory issue during backup

This commit is contained in:
Stefan Hardegger
2025-10-20 08:58:09 +02:00
parent 30c0132a92
commit 3dd2ff50d8

View File

@@ -7,7 +7,6 @@ import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -141,9 +140,12 @@ public class DatabaseManagementService implements ApplicationContextAware {
/**
* Create a comprehensive backup including database and files in ZIP format
* Returns a streaming resource to avoid loading large backups into memory
*/
public Resource createCompleteBackup() throws SQLException, IOException {
// Create temp file with deleteOnExit as safety net
Path tempZip = Files.createTempFile("storycove-backup", ".zip");
tempZip.toFile().deleteOnExit();
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(tempZip))) {
// 1. Add database dump
@@ -156,11 +158,30 @@ public class DatabaseManagementService implements ApplicationContextAware {
addMetadataToZip(zipOut);
}
// Return the ZIP file as a resource
byte[] zipData = Files.readAllBytes(tempZip);
// Return the ZIP file as a FileSystemResource for streaming
// This avoids loading the entire file into memory
return new org.springframework.core.io.FileSystemResource(tempZip.toFile()) {
@Override
public InputStream getInputStream() throws IOException {
// Wrap the input stream to delete the temp file after it's fully read
return new java.io.FilterInputStream(super.getInputStream()) {
@Override
public void close() throws IOException {
try {
super.close();
} finally {
// Clean up temp file after streaming is complete
try {
Files.deleteIfExists(tempZip);
return new ByteArrayResource(zipData);
} catch (IOException e) {
// Log but don't fail - deleteOnExit will handle it
System.err.println("Warning: Could not delete temp backup file: " + e.getMessage());
}
}
}
};
}
};
}
/**
@@ -289,20 +310,34 @@ public class DatabaseManagementService implements ApplicationContextAware {
System.err.println("PostgreSQL backup completed successfully");
// Read the backup file into memory
byte[] backupData = Files.readAllBytes(tempBackupFile);
return new ByteArrayResource(backupData);
// Return the backup file as a streaming resource to avoid memory issues with large databases
tempBackupFile.toFile().deleteOnExit();
return new org.springframework.core.io.FileSystemResource(tempBackupFile.toFile()) {
@Override
public InputStream getInputStream() throws IOException {
// Wrap the input stream to delete the temp file after it's fully read
return new java.io.FilterInputStream(super.getInputStream()) {
@Override
public void close() throws IOException {
try {
super.close();
} finally {
// Clean up temp file after streaming is complete
try {
Files.deleteIfExists(tempBackupFile);
} catch (IOException e) {
// Log but don't fail - deleteOnExit will handle it
System.err.println("Warning: Could not delete temp backup file: " + e.getMessage());
}
}
}
};
}
};
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Backup process was interrupted", e);
} finally {
// Clean up temporary file
try {
Files.deleteIfExists(tempBackupFile);
} catch (IOException e) {
System.err.println("Warning: Could not delete temporary backup file: " + e.getMessage());
}
}
}