Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Spanner copy backup #1530

Merged
merged 9 commits into from
Mar 24, 2022
Merged
102 changes: 102 additions & 0 deletions samples/backups-copy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright 2021 Google LLC
asthamohta marked this conversation as resolved.
Show resolved Hide resolved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// sample-metadata:
// title: Read data using an existing index.
// usage: node spannerCopyBackup <INSTANCE_ID> <DATABASE_ID> <PROJECT_ID>
asthamohta marked this conversation as resolved.
Show resolved Hide resolved

'use strict';

function main(
instanceId = 'my-instance',
databaseId = 'my-database',
backupId = 'my-backup',
sourceBackupId = 'my-source-backup',
projectId = 'my-project-id'
) {
// [START spanner_copy_backup]
/**
* TODO(developer): Uncomment these variables before running the sample.
*/
// const instanceId = 'my-instance';
// const databaseId = 'my-database';
// const backupId = 'my-backup',
// sourceBackupId = 'my-source-backup',
asthamohta marked this conversation as resolved.
Show resolved Hide resolved
// const projectId = 'my-project-id';

// Imports the Google Cloud Spanner client library
const {Spanner} = require('@google-cloud/spanner');
const {PreciseDate} = require('@google-cloud/precise-date');

// Instantiates a client
const spanner = new Spanner({
projectId: projectId,
});

async function spannerCopyBackup() {
// Gets a reference to a Cloud Spanner instance, database and backup
const instance = spanner.instance(instanceId);
const database = instance.database(databaseId);
const sourceBackup = instance.backup(sourceBackupId);

// Expire source and copy backup 14 days in the future
asthamohta marked this conversation as resolved.
Show resolved Hide resolved
const expireTime = Spanner.timestamp(
Date.now() + 1000 * 60 * 60 * 24 * 14
).toStruct();

// Copy the backup of the database
try {
console.log(
`Creating copy of the source backup ${sourceBackup.formattedName_}.`
);
const [, operation] = await instance.copyBackup(
sourceBackup.formattedName_,
backupId,
{
expireTime: expireTime,
}
);

console.log(
`Waiting for backup copy${instance.backup(backupId)} to complete...`
);
await operation.promise();

// Verify the copy backup is ready
const copyBackup = instance.backup(backupId);
const [copyBackupInfo] = await copyBackup.getMetadata();
if (copyBackupInfo.state === 'READY') {
console.log(
`Backup copy ${copyBackupInfo.name} of size ` +
`${copyBackupInfo.sizeBytes} bytes was created at ` +
`${new PreciseDate(copyBackupInfo.createTime).toISOString()}`
);
} else {
console.error('ERROR: Copy of backup is not ready.');
}
} catch (err) {
console.error('ERROR:', err);
} finally {
// Close the database when finished.
await database.close();
}
}
spannerCopyBackup();
// [END spanner_copy_backup]
}
process.on('unhandledRejection', err => {
console.error(err.message);
process.exitCode = 1;
});
main(...process.argv.slice(2));
9 changes: 7 additions & 2 deletions samples/backups-update.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,13 @@ async function updateBackup(instanceId, backupId, projectId) {
// Read backup metadata and update expiry time
try {
const currentExpireTime = await backup.getExpireTime();
const newExpireTime = new PreciseDate(currentExpireTime);
newExpireTime.setDate(newExpireTime.getDate() + 30);
const maxExpireTime = await backup.getMaxExpireTime();
const wantExpireTime = new PreciseDate(currentExpireTime);
wantExpireTime.setDate(wantExpireTime.getDate() + 1);
// New expire time should be less than the max expire time
const min = (currentExpireTime, maxExpireTime) =>
currentExpireTime < maxExpireTime ? currentExpireTime : maxExpireTime;
const newExpireTime = new PreciseDate(min(wantExpireTime, maxExpireTime));
console.log(
`Backup ${backupId} current expire time: ${currentExpireTime.toISOString()}`
);
Expand Down
15 changes: 15 additions & 0 deletions samples/system-test/spanner.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const VERSION_RETENTION_DATABASE_ID = `test-database-${CURRENT_TIME}-v`;
const ENCRYPTED_DATABASE_ID = `test-database-${CURRENT_TIME}-enc`;
const DEFAULT_LEADER_DATABASE_ID = `test-database-${CURRENT_TIME}-dl`;
const BACKUP_ID = `test-backup-${CURRENT_TIME}`;
const COPY_BACKUP_ID = `test-copy-backup-${CURRENT_TIME}`;
const ENCRYPTED_BACKUP_ID = `test-backup-${CURRENT_TIME}-enc`;
const CANCELLED_BACKUP_ID = `test-backup-${CURRENT_TIME}-c`;
const LOCATION_ID = 'regional-us-central1';
Expand All @@ -63,6 +64,7 @@ const DEFAULT_LEADER_2 = 'us-east1';

const spanner = new Spanner({
projectId: PROJECT_ID,
apiEndpoint: 'staging-wrenchworks.sandbox.googleapis.com',
asthamohta marked this conversation as resolved.
Show resolved Hide resolved
});
const LABEL = 'node-sample-tests';
const GAX_OPTIONS = {
Expand Down Expand Up @@ -214,6 +216,7 @@ describe('Spanner', () => {
await Promise.all([
instance.backup(BACKUP_ID).delete(GAX_OPTIONS),
instance.backup(ENCRYPTED_BACKUP_ID).delete(GAX_OPTIONS),
instance.backup(COPY_BACKUP_ID).delete(GAX_OPTIONS),
instance.backup(CANCELLED_BACKUP_ID).delete(GAX_OPTIONS),
]);
await instance.delete(GAX_OPTIONS);
Expand All @@ -223,6 +226,7 @@ describe('Spanner', () => {
instance.database(RESTORE_DATABASE_ID).delete(),
instance.database(ENCRYPTED_RESTORE_DATABASE_ID).delete(),
instance.backup(BACKUP_ID).delete(GAX_OPTIONS),
instance.backup(COPY_BACKUP_ID).delete(GAX_OPTIONS),
instance.backup(ENCRYPTED_BACKUP_ID).delete(GAX_OPTIONS),
instance.backup(CANCELLED_BACKUP_ID).delete(GAX_OPTIONS),
]);
Expand Down Expand Up @@ -1021,6 +1025,17 @@ describe('Spanner', () => {
assert.include(output, `using encryption key ${key.name}`);
});

// copy_backup
it('should create a copy of a backup', async () => {
const output = execSync(
`node backups-copy.js ${INSTANCE_ID} ${DATABASE_ID} ${COPY_BACKUP_ID} ${BACKUP_ID} ${PROJECT_ID}`
);
assert.match(
output,
new RegExp(`(.*)Backup copy(.*)${COPY_BACKUP_ID} of size(.*)`)
);
});

// cancel_backup
it('should cancel a backup of the database', async () => {
const output = execSync(
Expand Down
Loading