From 92a3324ca23b134dedc0f4798f7f00c2a6a78c4c Mon Sep 17 00:00:00 2001 From: Antonin Jousson <18756890+Antoninj@users.noreply.github.com> Date: Tue, 21 Jul 2026 21:50:11 +0200 Subject: [PATCH 1/4] fix: support configurable backup file prefix (#83) --- src/services/api/models/agent/status.rs | 2 ++ src/services/storage/providers/azure_blob/mod.rs | 2 +- .../storage/providers/google_cloud_storage/mod.rs | 2 +- src/services/storage/providers/google_drive/mod.rs | 2 +- src/services/storage/providers/local.rs | 2 +- src/services/storage/providers/s3/mod.rs | 2 +- src/tests/utils/file_tests.rs | 12 ++++++++---- src/utils/file.rs | 10 ++++++++-- 8 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/services/api/models/agent/status.rs b/src/services/api/models/agent/status.rs index 34557ac..293da97 100644 --- a/src/services/api/models/agent/status.rs +++ b/src/services/api/models/agent/status.rs @@ -24,6 +24,8 @@ pub struct DatabaseStorage { #[serde(deserialize_with = "deserialize_snake_case")] pub config: Value, pub provider: String, + #[serde(default)] + pub prefix: Option, } #[derive(Debug, Deserialize)] diff --git a/src/services/storage/providers/azure_blob/mod.rs b/src/services/storage/providers/azure_blob/mod.rs index b0b5e6b..9a06481 100644 --- a/src/services/storage/providers/azure_blob/mod.rs +++ b/src/services/storage/providers/azure_blob/mod.rs @@ -95,7 +95,7 @@ impl StorageProvider for AzureBlobProvider { }; let file_name = full_file_name(encrypt); - let remote_file_path = full_file_path(&file_name); + let remote_file_path = full_file_path(&file_name, storage.prefix.as_deref()); info!( "Starting block upload to azure blob {}/{}", config.container_name, remote_file_path diff --git a/src/services/storage/providers/google_cloud_storage/mod.rs b/src/services/storage/providers/google_cloud_storage/mod.rs index 453a63e..997a973 100644 --- a/src/services/storage/providers/google_cloud_storage/mod.rs +++ b/src/services/storage/providers/google_cloud_storage/mod.rs @@ -85,7 +85,7 @@ impl StorageProvider for GoogleCloudStorageProvider { let file_name = full_file_name(encrypt); info!("Uploading file {}", file_name); - let remote_file_path = full_file_path(&file_name); + let remote_file_path = full_file_path(&file_name, storage.prefix.as_deref()); let client = match build_client(&config).await { Ok(c) => c, diff --git a/src/services/storage/providers/google_drive/mod.rs b/src/services/storage/providers/google_drive/mod.rs index 89b11b5..25ab40e 100644 --- a/src/services/storage/providers/google_drive/mod.rs +++ b/src/services/storage/providers/google_drive/mod.rs @@ -85,7 +85,7 @@ impl StorageProvider for GoogleDriveProvider { info!("Uploading file {}", file_name); - let remote_file_path = full_file_path(&file_name); + let remote_file_path = full_file_path(&file_name, storage.prefix.as_deref()); match upload_stream_to_google_drive( &config, diff --git a/src/services/storage/providers/local.rs b/src/services/storage/providers/local.rs index a574faa..2dd4e5b 100644 --- a/src/services/storage/providers/local.rs +++ b/src/services/storage/providers/local.rs @@ -38,7 +38,7 @@ impl StorageProvider for LocalProvider { let encrypt = encrypt.unwrap_or(false); let file_name = full_file_name(encrypt); - let remote_file_path = full_file_path(&file_name); + let remote_file_path = full_file_path(&file_name, storage.prefix.as_deref()); let total_size = match fs::metadata(&file_path).await { Ok(meta) => meta.len(), diff --git a/src/services/storage/providers/s3/mod.rs b/src/services/storage/providers/s3/mod.rs index f01db64..6c85f5f 100644 --- a/src/services/storage/providers/s3/mod.rs +++ b/src/services/storage/providers/s3/mod.rs @@ -148,7 +148,7 @@ impl StorageProvider for S3Provider { info!("Uploading file {}", file_name); let bucket = &config.bucket_name; - let remote_file_path = full_file_path(&file_name); + let remote_file_path = full_file_path(&file_name, storage.prefix.as_deref()); info!("S3 key {:}", remote_file_path); info!( "Starting multipart upload to s3://{}/{}", diff --git a/src/tests/utils/file_tests.rs b/src/tests/utils/file_tests.rs index 697a5d5..5acb43a 100644 --- a/src/tests/utils/file_tests.rs +++ b/src/tests/utils/file_tests.rs @@ -30,12 +30,16 @@ fn full_file_name_matches_expected_suffix() { } #[test] -fn full_file_path_prefixes_backups_directory_and_date() { +fn full_file_path_uses_default_or_configured_prefix() { let file_name = "backup.tar.gz".to_string(); - let full_path = full_file_path(&file_name); - assert!(full_path.starts_with("backups/")); - assert!(full_path.ends_with("/backup.tar.gz")); + let default_path = full_file_path(&file_name, None); + assert!(default_path.starts_with("backups/")); + assert!(default_path.ends_with("/backup.tar.gz")); + + let configured_path = full_file_path(&file_name, Some("/portabase/")); + assert!(configured_path.starts_with("portabase/")); + assert!(configured_path.ends_with("/backup.tar.gz")); } #[tokio::test] diff --git a/src/utils/file.rs b/src/utils/file.rs index 13f0ea2..0949afc 100644 --- a/src/utils/file.rs +++ b/src/utils/file.rs @@ -47,8 +47,14 @@ pub fn full_file_name(encrypt: bool) -> String { } } -pub fn full_file_path(file_name: &String) -> String { - format!("backups/{}/{}", Utc::now().format("%Y-%m-%d"), file_name) +pub fn full_file_path(file_name: &String, prefix: Option<&str>) -> String { + let prefix = prefix + .map(str::trim) + .map(|prefix| prefix.trim_matches(char::from(47))) + .filter(|prefix| !prefix.is_empty()) + .unwrap_or("backups"); + + format!("{}/{}/{}", prefix, Utc::now().format("%Y-%m-%d"), file_name) } const CHUNK_SIZE: usize = 16 * 1024 * 1024; From 37b4c1fe6db66c2bb9b002d07051f22998bf98c6 Mon Sep 17 00:00:00 2001 From: charles-gauthereau Date: Tue, 21 Jul 2026 23:05:59 +0200 Subject: [PATCH 2/4] fix: backup folder name --- docker-compose.yml | 4 +++- src/services/api/models/agent/status.rs | 4 ++-- src/services/storage/providers/azure_blob/mod.rs | 2 +- .../storage/providers/google_cloud_storage/mod.rs | 2 +- src/services/storage/providers/google_drive/mod.rs | 2 +- src/services/storage/providers/local.rs | 2 +- src/services/storage/providers/s3/mod.rs | 2 +- src/tests/utils/file_tests.rs | 2 +- src/utils/file.rs | 10 +++++----- 9 files changed, 16 insertions(+), 14 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index ff72c6c..d4e1bae 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,11 +15,13 @@ services: # - cargo-target:/app/target - databases_sqlite-data:/sqlite-data/workspace/data - ./scripts/sqlite/test-db:/sqlite-data-2/workspace/data +# - /bigdisk:/scratch environment: APP_ENV: development LOG: debug TZ: "Europe/Paris" - EDGE_KEY: "eyJzZXJ2ZXJVcmwiOiJodHRwOi8vbG9jYWxob3N0Ojg4ODciLCJhZ2VudElkIjoiZWZhYTM0YTQtZDY1NC00OGQ3LTgwNDYtNjRkMWExYTA1M2FlIiwibWFzdGVyS2V5QjY0IjoiMUh0djdtWCtYVkJxL0IzUEV2WDlZZjlQeUdVZW5oRHlXemo5THRqNW90WT0ifQ==" +# TMPDIR: /scratch + EDGE_KEY: "eyJzZXJ2ZXJVcmwiOiJodHRwOi8vbG9jYWxob3N0Ojg4ODciLCJhZ2VudElkIjoiNTljYzRjYTUtOTAyNy00ZThiLTk1NDktMjAzOTI3ZDVjNmUyIiwibWFzdGVyS2V5QjY0IjoiMUh0djdtWCtYVkJxL0IzUEV2WDlZZjlQeUdVZW5oRHlXemo5THRqNW90WT0ifQ==" #CHUNK_SIZE_MB: "1" #POOLING: 1 #DATABASES_CONFIG_FILE: "config.toml" diff --git a/src/services/api/models/agent/status.rs b/src/services/api/models/agent/status.rs index 293da97..d8c4ea3 100644 --- a/src/services/api/models/agent/status.rs +++ b/src/services/api/models/agent/status.rs @@ -24,8 +24,8 @@ pub struct DatabaseStorage { #[serde(deserialize_with = "deserialize_snake_case")] pub config: Value, pub provider: String, - #[serde(default)] - pub prefix: Option, + #[serde(default, rename = "folderName")] + pub folder_name: Option, } #[derive(Debug, Deserialize)] diff --git a/src/services/storage/providers/azure_blob/mod.rs b/src/services/storage/providers/azure_blob/mod.rs index 9a06481..c9762f2 100644 --- a/src/services/storage/providers/azure_blob/mod.rs +++ b/src/services/storage/providers/azure_blob/mod.rs @@ -95,7 +95,7 @@ impl StorageProvider for AzureBlobProvider { }; let file_name = full_file_name(encrypt); - let remote_file_path = full_file_path(&file_name, storage.prefix.as_deref()); + let remote_file_path = full_file_path(&file_name, storage.folder_name.as_deref()); info!( "Starting block upload to azure blob {}/{}", config.container_name, remote_file_path diff --git a/src/services/storage/providers/google_cloud_storage/mod.rs b/src/services/storage/providers/google_cloud_storage/mod.rs index 997a973..0220a09 100644 --- a/src/services/storage/providers/google_cloud_storage/mod.rs +++ b/src/services/storage/providers/google_cloud_storage/mod.rs @@ -85,7 +85,7 @@ impl StorageProvider for GoogleCloudStorageProvider { let file_name = full_file_name(encrypt); info!("Uploading file {}", file_name); - let remote_file_path = full_file_path(&file_name, storage.prefix.as_deref()); + let remote_file_path = full_file_path(&file_name, storage.folder_name.as_deref()); let client = match build_client(&config).await { Ok(c) => c, diff --git a/src/services/storage/providers/google_drive/mod.rs b/src/services/storage/providers/google_drive/mod.rs index 25ab40e..e127414 100644 --- a/src/services/storage/providers/google_drive/mod.rs +++ b/src/services/storage/providers/google_drive/mod.rs @@ -85,7 +85,7 @@ impl StorageProvider for GoogleDriveProvider { info!("Uploading file {}", file_name); - let remote_file_path = full_file_path(&file_name, storage.prefix.as_deref()); + let remote_file_path = full_file_path(&file_name, storage.folder_name.as_deref()); match upload_stream_to_google_drive( &config, diff --git a/src/services/storage/providers/local.rs b/src/services/storage/providers/local.rs index 2dd4e5b..b93920a 100644 --- a/src/services/storage/providers/local.rs +++ b/src/services/storage/providers/local.rs @@ -38,7 +38,7 @@ impl StorageProvider for LocalProvider { let encrypt = encrypt.unwrap_or(false); let file_name = full_file_name(encrypt); - let remote_file_path = full_file_path(&file_name, storage.prefix.as_deref()); + let remote_file_path = full_file_path(&file_name, storage.folder_name.as_deref()); let total_size = match fs::metadata(&file_path).await { Ok(meta) => meta.len(), diff --git a/src/services/storage/providers/s3/mod.rs b/src/services/storage/providers/s3/mod.rs index 6c85f5f..8ef436e 100644 --- a/src/services/storage/providers/s3/mod.rs +++ b/src/services/storage/providers/s3/mod.rs @@ -148,7 +148,7 @@ impl StorageProvider for S3Provider { info!("Uploading file {}", file_name); let bucket = &config.bucket_name; - let remote_file_path = full_file_path(&file_name, storage.prefix.as_deref()); + let remote_file_path = full_file_path(&file_name, storage.folder_name.as_deref()); info!("S3 key {:}", remote_file_path); info!( "Starting multipart upload to s3://{}/{}", diff --git a/src/tests/utils/file_tests.rs b/src/tests/utils/file_tests.rs index 5acb43a..b3b0ee0 100644 --- a/src/tests/utils/file_tests.rs +++ b/src/tests/utils/file_tests.rs @@ -30,7 +30,7 @@ fn full_file_name_matches_expected_suffix() { } #[test] -fn full_file_path_uses_default_or_configured_prefix() { +fn full_file_path_uses_default_or_configured_folder_name() { let file_name = "backup.tar.gz".to_string(); let default_path = full_file_path(&file_name, None); diff --git a/src/utils/file.rs b/src/utils/file.rs index 0949afc..b39dd92 100644 --- a/src/utils/file.rs +++ b/src/utils/file.rs @@ -47,14 +47,14 @@ pub fn full_file_name(encrypt: bool) -> String { } } -pub fn full_file_path(file_name: &String, prefix: Option<&str>) -> String { - let prefix = prefix +pub fn full_file_path(file_name: &String, folder_name: Option<&str>) -> String { + let folder_name = folder_name .map(str::trim) - .map(|prefix| prefix.trim_matches(char::from(47))) - .filter(|prefix| !prefix.is_empty()) + .map(|folder_name| folder_name.trim_matches(char::from(47))) + .filter(|folder_name| !folder_name.is_empty()) .unwrap_or("backups"); - format!("{}/{}/{}", prefix, Utc::now().format("%Y-%m-%d"), file_name) + format!("{}/{}/{}", folder_name, Utc::now().format("%Y-%m-%d"), file_name) } const CHUNK_SIZE: usize = 16 * 1024 * 1024; From c186d571056649970e50d2afca9f9623318e0eeb Mon Sep 17 00:00:00 2001 From: charles-gauthereau Date: Tue, 21 Jul 2026 23:10:22 +0200 Subject: [PATCH 3/4] fix: ghcr publish agent image --- .github/workflows/ghcr.yml | 72 +++++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 10 +++++ 2 files changed, 82 insertions(+) create mode 100644 .github/workflows/ghcr.yml diff --git a/.github/workflows/ghcr.yml b/.github/workflows/ghcr.yml new file mode 100644 index 0000000..831a2e7 --- /dev/null +++ b/.github/workflows/ghcr.yml @@ -0,0 +1,72 @@ +name: GHCR Publish + +on: + workflow_call: + inputs: + version: + required: true + type: string + ref: + required: true + type: string + add_latest: + required: false + type: boolean + default: false + dockerfile: + required: false + type: string + default: "./docker/Dockerfile" + target: + required: false + type: string + default: "prod" + +permissions: + contents: read + packages: write + +jobs: + publish: + name: Build and push to GHCR + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref }} + fetch-depth: 0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository_owner }}/agent + tags: | + type=semver,pattern={{version}},value=${{ inputs.version }} + type=semver,pattern={{major}}.{{minor}},value=${{ inputs.version }} + type=semver,pattern={{major}},value=${{ inputs.version }} + type=raw,value=latest,enable=${{ inputs.add_latest }} + + - name: Build and push image + uses: docker/build-push-action@v6 + with: + context: . + file: ${{ inputs.dockerfile }} + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} + target: ${{ inputs.target }} + provenance: false + cache-from: type=gha,scope=ghcr-build + cache-to: type=gha,mode=max,scope=ghcr-build,ignore-error=true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 84131e9..2b80707 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -100,6 +100,15 @@ jobs: DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} + publish-docker-ghcr: + needs: create-release + if: ${{ needs.create-release.result == 'success' }} + uses: ./.github/workflows/ghcr.yml + with: + version: ${{ needs.create-release.outputs.version }} + ref: ${{ needs.create-release.outputs.version }} + add_latest: true + publish-helm: needs: create-release if: ${{ needs.create-release.result == 'success' }} @@ -113,6 +122,7 @@ jobs: needs: - create-release - publish-docker + - publish-docker-ghcr - publish-helm runs-on: ubuntu-latest outputs: From 80b1c0dac08d0f84898e533f8636a6746338fe24 Mon Sep 17 00:00:00 2001 From: charles-gauthereau Date: Fri, 24 Jul 2026 18:29:04 +0200 Subject: [PATCH 4/4] fix: firebird with -g for gbak --- justfile | 8 + scripts/firebird/seed-large.sql | 259 ++++++++++++++++++++++++++++++++ src/domain/firebird/backup.rs | 1 + 3 files changed, 268 insertions(+) create mode 100644 scripts/firebird/seed-large.sql diff --git a/justfile b/justfile index 8a98edf..3063c15 100644 --- a/justfile +++ b/justfile @@ -45,6 +45,14 @@ seed-firebird: echo "SELECT RDB\$RELATION_NAME FROM RDB\$RELATIONS WHERE RDB\$SYSTEM_FLAG = 0 AND RDB\$VIEW_BLR IS NULL;" \ | docker exec -i db-firebird isql -user alice -password fake_password /var/lib/firebird/data/mirror.fdb +seed-firebird-large: + echo "Seeding Firebird..." + docker exec -i db-firebird isql -user alice -password fake_password /var/lib/firebird/data/mirror.fdb < ./scripts/firebird/seed-large.sql + + echo "Verifying Firebird tables..." + echo "SELECT RDB\$RELATION_NAME FROM RDB\$RELATIONS WHERE RDB\$SYSTEM_FLAG = 0 AND RDB\$VIEW_BLR IS NULL;" \ + | docker exec -i db-firebird isql -user alice -password fake_password /var/lib/firebird/data/mirror.fdb + seed-mssql: echo "Seeding MSSQL..." docker exec -i rust-dev sqlcmd -S "db-mssql,1433" -U sa -P "$MSSQL_SA_PASSWORD" -N disable -i /app/scripts/mssql/seed.sql diff --git a/scripts/firebird/seed-large.sql b/scripts/firebird/seed-large.sql new file mode 100644 index 0000000..4492faa --- /dev/null +++ b/scripts/firebird/seed-large.sql @@ -0,0 +1,259 @@ +SET SQL DIALECT 3; +SET BAIL ON; +SET AUTODDL OFF; + +CREATE TABLE users ( + id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + email VARCHAR(255) NOT NULL UNIQUE, + name VARCHAR(255), + payload BLOB SUB_TYPE TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +COMMIT; + +INSERT INTO users (email, name, payload) +VALUES ('alice@example.com', 'Alice', 'Alice seed data'); + +INSERT INTO users (email, name, payload) +VALUES ('bob@example.com', 'Bob', 'Bob seed data'); + +COMMIT; + +/* + * Each procedure call generates approximately 128 MiB: + * + * 128 rows + * × 128 chunks per row + * × 8191 bytes per chunk + * = approximately 128 MiB + * + * 40 calls = approximately 5 GiB. + */ + +SET TERM ^; + +CREATE PROCEDURE seed_users_batch ( + p_rows INTEGER, + p_chunks_per_row INTEGER +) + AS +DECLARE VARIABLE v_row_index INTEGER; + DECLARE VARIABLE v_chunk_index INTEGER; + DECLARE VARIABLE v_uuid VARCHAR(36); + DECLARE VARIABLE v_chunk VARCHAR(8191); + DECLARE VARIABLE v_payload BLOB SUB_TYPE TEXT; +BEGIN + v_row_index = 0; + + WHILE (v_row_index < p_rows) DO +BEGIN + v_payload = NULL; + v_chunk_index = 0; + + WHILE (v_chunk_index < p_chunks_per_row) DO +BEGIN + /* + * Generate a different chunk to avoid producing a completely + * uniform BLOB. + */ + v_chunk = RPAD( + UUID_TO_CHAR(GEN_UUID()), + 8191, + UUID_TO_CHAR(GEN_UUID()) + ); + + v_payload = BLOB_APPEND(v_payload, v_chunk); + v_chunk_index = v_chunk_index + 1; +END + + v_uuid = UUID_TO_CHAR(GEN_UUID()); + +INSERT INTO users ( + email, + name, + payload +) +VALUES ( + :v_uuid || '@example.test', + 'Seed User ' || :v_uuid, + :v_payload + ); + +v_row_index = v_row_index + 1; +END +END^ + +SET TERM ;^ + +/* Batch 01 — approximately 128 MiB */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 02 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 03 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 04 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 05 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 06 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 07 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 08 — approximately 1 GiB total */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 09 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 10 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 11 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 12 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 13 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 14 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 15 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 16 — approximately 2 GiB total */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 17 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 18 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 19 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 20 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 21 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 22 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 23 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 24 — approximately 3 GiB total */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 25 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 26 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 27 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 28 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 29 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 30 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 31 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 32 — approximately 4 GiB total */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 33 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 34 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 35 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 36 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 37 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 38 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 39 */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +/* Batch 40 — approximately 5 GiB total */ +EXECUTE PROCEDURE seed_users_batch(128, 128); +COMMIT; + +DROP PROCEDURE seed_users_batch; + +COMMIT; + +SELECT + COUNT(*) AS user_count, + CAST(SUM(OCTET_LENGTH(payload)) / 1073741824.0 AS DECIMAL(18, 2)) + AS payload_size_gib +FROM users; + +COMMIT; \ No newline at end of file diff --git a/src/domain/firebird/backup.rs b/src/domain/firebird/backup.rs index 959bf9e..3d34dad 100644 --- a/src/domain/firebird/backup.rs +++ b/src/domain/firebird/backup.rs @@ -23,6 +23,7 @@ pub async fn run( let start = Instant::now(); let output = Command::new("gbak") .arg("-b") + .arg("-g") .arg("-v") .arg("-user").arg(&cfg.username) .arg("-password").arg(&cfg.password)