Memulihkan data menggunakan pemulihan point-in-time (PITR)

Halaman ini menjelaskan cara menggunakan pemulihan point-in-time (PITR) untuk mempertahankan dan memulihkan data di Spanner untuk database dialek GoogleSQL dan database dialek PostgreSQL.

Untuk mempelajari lebih lanjut, lihat Pemulihan point-in-time.

Prasyarat

Panduan ini menggunakan database dan skema seperti yang ditentukan dalam mulai cepat Spanner. Anda dapat menjalankan panduan memulai cepat untuk membuat database dan skema, atau mengubah perintah untuk digunakan dengan database Anda sendiri.

Tetapkan periode retensi data

Untuk menetapkan periode retensi database Anda:

Konsol

  1. Buka halaman Spanner Instances di konsol Google Cloud .

    Buka halaman Spanner Instances

  2. Klik instance yang berisi database untuk membuka halaman Ringkasan.

  3. Klik database untuk membuka halaman Ringkasan.

  4. Pilih tab Backup/Restore.

    Formulir Pencadangan/Pemulihan di konsol, yang menampilkan kolom Periode retensi versi yang ditetapkan ke 1 jam dan ikon pensil untuk mengedit nilai ini.

  5. Klik ikon pensil di kolom Periode retensi versi.

  6. Masukkan jumlah dan satuan waktu untuk periode retensi data, lalu klik Perbarui.

gcloud

Perbarui skema database dengan pernyataan ALTER DATABASE. Contoh:

gcloud spanner databases ddl update example-db \
    --instance=test-instance \
    --ddl="ALTER DATABASE \"example-db\" \
    SET spanner.version_retention_period = '7d';"

Berikut adalah output-nya:

ALTER DATABASE "example-db" SET "spanner.version_retention_period" = '7d';

Untuk melihat periode retensi, dapatkan DDL database Anda:

gcloud spanner databases ddl describe example-db \
    --instance=test-instance

Berikut adalah output-nya:

ALTER DATABASE example-db SET OPTIONS (
  version_retention_period = '7d'
);
...

Library klien

C#


using Google.Cloud.Spanner.Data;
using System.Threading.Tasks;

public class CreateDatabaseWithRetentionPeriodAsyncSample
{
    public async Task CreateDatabaseWithRetentionPeriodAsync(string projectId, string instanceId, string databaseId)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}";

        using var connection = new SpannerConnection(connectionString);
        var versionRetentionPeriod = "7d";
        var createStatement = $"CREATE DATABASE `{databaseId}`";
        var alterStatement = @$"ALTER DATABASE `{databaseId}` SET OPTIONS
                   (version_retention_period = '{versionRetentionPeriod}')";
        // The retention period cannot be set as part of the CREATE DATABASE statement,
        // but can be set using an ALTER DATABASE statement directly after database creation.
        using var createDbCommand = connection.CreateDdlCommand(
            createStatement,
            alterStatement
        );
        await createDbCommand.ExecuteNonQueryAsync();
    }
}

C++

void CreateDatabaseWithVersionRetentionPeriod(
    google::cloud::spanner_admin::DatabaseAdminClient client,
    std::string const& project_id, std::string const& instance_id,
    std::string const& database_id) {
  google::cloud::spanner::Database database(project_id, instance_id,
                                            database_id);
  google::spanner::admin::database::v1::CreateDatabaseRequest request;
  request.set_parent(database.instance().FullName());
  request.set_create_statement("CREATE DATABASE `" + database.database_id() +
                               "`");
  request.add_extra_statements("ALTER DATABASE `" + database.database_id() +
                               "` " +
                               "SET OPTIONS (version_retention_period='2h')");
  request.add_extra_statements(R"""(
      CREATE TABLE Singers (
          SingerId   INT64 NOT NULL,
          FirstName  STRING(1024),
          LastName   STRING(1024),
          SingerInfo BYTES(MAX),
          FullName   STRING(2049)
              AS (ARRAY_TO_STRING([FirstName, LastName], " ")) STORED
      ) PRIMARY KEY (SingerId))""");
  request.add_extra_statements(R"""(
      CREATE TABLE Albums (
          SingerId     INT64 NOT NULL,
          AlbumId      INT64 NOT NULL,
          AlbumTitle   STRING(MAX)
      ) PRIMARY KEY (SingerId, AlbumId),
          INTERLEAVE IN PARENT Singers ON DELETE CASCADE)""");
  auto db = client.CreateDatabase(request).get();
  if (!db) throw std::move(db).status();
  std::cout << "Database " << db->name() << " created.\n";

  auto ddl = client.GetDatabaseDdl(db->name());
  if (!ddl) throw std::move(ddl).status();
  std::cout << "Database DDL is:\n" << ddl->DebugString();
}

Go

import (
	"context"
	"fmt"
	"io"
	"regexp"

	database "cloud.google.com/go/spanner/admin/database/apiv1"
	adminpb "cloud.google.com/go/spanner/admin/database/apiv1/databasepb"
)

func createDatabaseWithRetentionPeriod(ctx context.Context, w io.Writer, db string) error {
	matches := regexp.MustCompile("^(.+)/databases/(.+)$").FindStringSubmatch(db)
	if matches == nil || len(matches) != 3 {
		return fmt.Errorf("createDatabaseWithRetentionPeriod: invalid database id %q", db)
	}

	adminClient, err := database.NewDatabaseAdminClient(ctx)
	if err != nil {
		return fmt.Errorf("createDatabaseWithRetentionPeriod.NewDatabaseAdminClient: %w", err)
	}
	defer adminClient.Close()

	// Create a database with a version retention period of 7 days.
	retentionPeriod := "7d"
	alterDatabase := fmt.Sprintf(
		"ALTER DATABASE `%s` SET OPTIONS (version_retention_period = '%s')",
		matches[2], retentionPeriod,
	)
	req := adminpb.CreateDatabaseRequest{
		Parent:          matches[1],
		CreateStatement: "CREATE DATABASE `" + matches[2] + "`",
		ExtraStatements: []string{
			`CREATE TABLE Singers (
				SingerId   INT64 NOT NULL,
				FirstName  STRING(1024),
				LastName   STRING(1024),
				SingerInfo BYTES(MAX)
			) PRIMARY KEY (SingerId)`,
			`CREATE TABLE Albums (
				SingerId     INT64 NOT NULL,
				AlbumId      INT64 NOT NULL,
				AlbumTitle   STRING(MAX)
			) PRIMARY KEY (SingerId, AlbumId),
			INTERLEAVE IN PARENT Singers ON DELETE CASCADE`,
			alterDatabase,
		},
	}
	op, err := adminClient.CreateDatabase(ctx, &req)
	if err != nil {
		return fmt.Errorf("createDatabaseWithRetentionPeriod.CreateDatabase: %w", err)
	}
	if _, err := op.Wait(ctx); err != nil {
		return fmt.Errorf("createDatabaseWithRetentionPeriod.Wait: %w", err)
	}
	fmt.Fprintf(w, "Created database [%s] with version retention period %q\n", db, retentionPeriod)
	return nil
}

Java


import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerException;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
import com.google.common.collect.Lists;
import com.google.spanner.admin.database.v1.CreateDatabaseRequest;
import com.google.spanner.admin.database.v1.Database;
import com.google.spanner.admin.database.v1.InstanceName;
import java.util.concurrent.ExecutionException;

public class CreateDatabaseWithVersionRetentionPeriodSample {

  static void createDatabaseWithVersionRetentionPeriod() {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "my-project";
    String instanceId = "my-instance";
    String databaseId = "my-database";
    String versionRetentionPeriod = "7d";

    createDatabaseWithVersionRetentionPeriod(projectId, instanceId, databaseId,
        versionRetentionPeriod);
  }

  static void createDatabaseWithVersionRetentionPeriod(String projectId,
      String instanceId, String databaseId, String versionRetentionPeriod) {
    try (Spanner spanner =
        SpannerOptions.newBuilder().setProjectId(projectId).build().getService();
        DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient()) {
      CreateDatabaseRequest request =
          CreateDatabaseRequest.newBuilder()
              .setParent(InstanceName.of(projectId, instanceId).toString())
              .setCreateStatement("CREATE DATABASE `" + databaseId + "`")
              .addAllExtraStatements(Lists.newArrayList("ALTER DATABASE " + "`" + databaseId + "`"
                  + " SET OPTIONS ( version_retention_period = '" + versionRetentionPeriod + "' )"))
              .build();
      Database database =
          databaseAdminClient.createDatabaseAsync(request).get();
      System.out.println("Created database [" + database.getName() + "]");
      System.out.println("\tVersion retention period: " + database.getVersionRetentionPeriod());
      System.out.println("\tEarliest version time: " + database.getEarliestVersionTime());
    } catch (ExecutionException e) {
      // If the operation failed during execution, expose the cause.
      throw (SpannerException) e.getCause();
    } catch (InterruptedException e) {
      // Throw when a thread is waiting, sleeping, or otherwise occupied,
      // and the thread is interrupted, either before or during the activity.
      throw SpannerExceptionFactory.propagateInterrupt(e);
    }
  }
}

Node.js


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

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';

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

// Gets a reference to a Cloud Spanner Database Admin Client object
const databaseAdminClient = spanner.getDatabaseAdminClient();

try {
  // Create a new database with an extra statement which will alter the
  // database after creation to set the version retention period.
  console.log(
    `Creating database ${databaseAdminClient.instancePath(
      projectId,
      instanceId,
    )}.`,
  );
  const versionRetentionStatement = `
    ALTER DATABASE \`${databaseId}\`
    SET OPTIONS (version_retention_period = '1d')`;

  const [operation] = await databaseAdminClient.createDatabase({
    createStatement: 'CREATE DATABASE `' + databaseId + '`',
    extraStatements: [versionRetentionStatement],
    parent: databaseAdminClient.instancePath(projectId, instanceId),
  });

  console.log(`Waiting for operation on ${databaseId} to complete...`);
  await operation.promise();
  console.log(`
      Created database ${databaseId} with version retention period.`);

  const [metadata] = await databaseAdminClient.getDatabase({
    name: databaseAdminClient.databasePath(projectId, instanceId, databaseId),
  });

  console.log(`Version retention period: ${metadata.versionRetentionPeriod}`);
  const milliseconds =
    parseInt(metadata.earliestVersionTime.seconds, 10) * 1000 +
    parseInt(metadata.earliestVersionTime.nanos, 10) / 1e6;
  const date = new Date(milliseconds);
  console.log(`Earliest version time: ${date.toString()}`);
} catch (err) {
  console.error('ERROR:', err);
}

PHP

use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient;
use Google\Cloud\Spanner\Admin\Database\V1\CreateDatabaseRequest;
use Google\Cloud\Spanner\Admin\Database\V1\GetDatabaseRequest;

/**
 * Creates a database with data retention for Point In Time Restore.
 * Example:
 * ```
 * create_database_with_version_retention_period($projectId, $instanceId, $databaseId, $retentionPeriod);
 * ```
 *
 * @param string $projectId The Google Cloud project ID.
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 * @param string $retentionPeriod The data retention period for the database.
 */
function create_database_with_version_retention_period(
    string $projectId,
    string $instanceId,
    string $databaseId,
    string $retentionPeriod
): void {
    $databaseAdminClient = new DatabaseAdminClient();
    $instance = $databaseAdminClient->instanceName($projectId, $instanceId);
    $databaseFullName = $databaseAdminClient->databaseName($projectId, $instanceId, $databaseId);

    $operation = $databaseAdminClient->createDatabase(
        new CreateDatabaseRequest([
            'parent' => $instance,
            'create_statement' => sprintf('CREATE DATABASE `%s`', $databaseId),
            'extra_statements' => [
                'CREATE TABLE Singers (' .
                'SingerId     INT64 NOT NULL,' .
                'FirstName    STRING(1024),' .
                'LastName     STRING(1024),' .
                'SingerInfo   BYTES(MAX)' .
                ') PRIMARY KEY (SingerId)',
                'CREATE TABLE Albums (' .
                    'SingerId     INT64 NOT NULL,' .
                    'AlbumId      INT64 NOT NULL,' .
                    'AlbumTitle   STRING(MAX)' .
                ') PRIMARY KEY (SingerId, AlbumId),' .
                'INTERLEAVE IN PARENT Singers ON DELETE CASCADE',
                "ALTER DATABASE `$databaseId` SET OPTIONS(version_retention_period='$retentionPeriod')"
            ]
        ])
    );

    print('Waiting for operation to complete...' . PHP_EOL);
    $operation->pollUntilComplete();

    $request = new GetDatabaseRequest(['name' => $databaseFullName]);
    $databaseInfo = $databaseAdminClient->getDatabase($request);

    print(sprintf(
        'Database %s created with version retention period %s',
        $databaseInfo->getName(), $databaseInfo->getVersionRetentionPeriod()
    ) . PHP_EOL);
}

Python

def create_database_with_version_retention_period(
    instance_id, database_id, retention_period
):
    """Creates a database with a version retention period."""

    from google.cloud.spanner_admin_database_v1.types import spanner_database_admin

    spanner_client = spanner.Client()
    database_admin_api = spanner_client.database_admin_api
    ddl_statements = [
        "CREATE TABLE Singers ("
        + "  SingerId   INT64 NOT NULL,"
        + "  FirstName  STRING(1024),"
        + "  LastName   STRING(1024),"
        + "  SingerInfo BYTES(MAX)"
        + ") PRIMARY KEY (SingerId)",
        "CREATE TABLE Albums ("
        + "  SingerId     INT64 NOT NULL,"
        + "  AlbumId      INT64 NOT NULL,"
        + "  AlbumTitle   STRING(MAX)"
        + ") PRIMARY KEY (SingerId, AlbumId),"
        + "  INTERLEAVE IN PARENT Singers ON DELETE CASCADE",
        "ALTER DATABASE `{}`"
        " SET OPTIONS (version_retention_period = '{}')".format(
            database_id, retention_period
        ),
    ]
    operation = database_admin_api.create_database(
        request=spanner_database_admin.CreateDatabaseRequest(
            parent=database_admin_api.instance_path(
                spanner_client.project, instance_id
            ),
            create_statement="CREATE DATABASE `{}`".format(database_id),
            extra_statements=ddl_statements,
        )
    )

    db = operation.result(30)
    print(
        "Database {} created with version retention period {} and earliest version time {}".format(
            db.name, db.version_retention_period, db.earliest_version_time
        )
    )

    database_admin_api.drop_database(
        spanner_database_admin.DropDatabaseRequest(database=db.name)
    )

Ruby

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"
require "google/cloud/spanner/admin/database"

database_admin_client = Google::Cloud::Spanner::Admin::Database.database_admin

instance_path = database_admin_client.instance_path project: project_id, instance: instance_id

version_retention_period = "7d"

db_path = database_admin_client.database_path project: project_id,
                                              instance: instance_id,
                                              database: database_id

job = database_admin_client.create_database parent: instance_path,
                                            create_statement: "CREATE DATABASE `#{database_id}`",
                                            extra_statements: [
                                              "CREATE TABLE Singers (
    SingerId     INT64 NOT NULL,
    FirstName    STRING(1024),
    LastName     STRING(1024),
    SingerInfo   BYTES(MAX)
  ) PRIMARY KEY (SingerId)",

                                              "CREATE TABLE Albums (
    SingerId     INT64 NOT NULL,
    AlbumId      INT64 NOT NULL,
    AlbumTitle   STRING(MAX)
  ) PRIMARY KEY (SingerId, AlbumId),
  INTERLEAVE IN PARENT Singers ON DELETE CASCADE",

                                              "ALTER DATABASE `#{database_id}`
    SET OPTIONS ( version_retention_period = '#{version_retention_period}' )"
                                            ]

puts "Waiting for create database operation to complete"

job.wait_until_done!
database = database_admin_client.get_database name: db_path

puts "Created database #{database_id} on instance #{instance_id}"
puts "\tVersion retention period: #{database.version_retention_period}"
puts "\tEarliest version time: #{database.earliest_version_time}"

Catatan penggunaan:

  • Periode retensi data harus antara 1 jam dan 7 hari, dan dapat ditentukan dalam hari, jam, menit, atau detik. Misalnya, nilai 1d, 24h, 1440m, dan 86400s setara.
  • Jika Anda telah mengaktifkan logging untuk Spanner API di project Anda, peristiwa dicatat sebagai UpdateDatabaseDdl dan dapat dilihat di Logs Explorer.
  • Untuk kembali ke periode retensi default 1 jam, Anda dapat menetapkan opsi database version_retention_period ke NULL untuk database GoogleSQL atau DEFAULT untuk database PostgreSQL.
  • Saat Anda memperpanjang periode retensi data, sistem tidak akan mengisi ulang versi data sebelumnya. Misalnya, jika Anda memperpanjang periode retensi dari satu jam menjadi 24 jam, Anda harus menunggu 23 jam agar sistem mengumpulkan data lama sebelum Anda dapat memulihkan data dari 24 jam yang lalu.

Mendapatkan periode retensi data dan waktu versi paling awal

Aset Database mencakup dua kolom:

  • version_retention_period: periode ketika Spanner menyimpan semua versi data untuk database.
  • earliest_version_time: stempel waktu paling awal saat versi data sebelumnya dapat dibaca dari database. Nilai ini terus diperbarui oleh Spanner dan menjadi tidak berlaku saat dikueri. Jika Anda menggunakan nilai ini untuk memulihkan data, pastikan untuk memperhitungkan waktu sejak saat nilai dikueri hingga saat Anda memulai pemulihan.

Konsol

  1. Buka halaman Spanner Instances di konsol Google Cloud .

    Buka halaman Spanner Instances

  2. Klik instance yang berisi database untuk membuka halaman Ringkasan.

  3. Klik database untuk membuka halaman Ringkasan.

  4. Pilih tab Backup/Restore untuk membuka halaman Backup/Restore dan menampilkan periode retensi data.

    Formulir Pencadangan/Pemulihan di konsol, yang menampilkan kolom Periode retensi versi yang ditetapkan ke 1 jam dan ikon pensil untuk mengedit nilai ini.

  5. Klik Buat untuk membuka halaman Buat cadangan dan menampilkan waktu versi paling awal.

    Buat formulir pencadangan di konsol dengan opsi Buat cadangan dari waktu sebelumnya dicentang dan menampilkan waktu versi paling awal.

gcloud

Anda bisa mendapatkan kolom ini dengan memanggil describe databases atau list databases. Contoh:

gcloud spanner databases describe example-db \
    --instance=test-instance

Berikut adalah output-nya:

createTime: '2020-09-07T16:56:08.285140Z'
earliestVersionTime: '2020-10-07T16:56:08.285140Z'
name: projects/my-project/instances/test-instance/databases/example-db
state: READY
versionRetentionPeriod: 3d

Memulihkan sebagian database Anda

  1. Lakukan stale read dan tentukan stempel waktu pemulihan yang diperlukan. Pastikan stempel waktu yang Anda tentukan lebih baru daripada earliest_version_time. database

    gcloud

    Gunakan execute-sql Misalnya:

    gcloud spanner databases execute-sql example-db \
        --instance=test-instance \
        --read-timestamp=2020-09-11T10:19:36.010459-07:00 \
        --sql='SELECT * FROM SINGERS'

    Library klien

    Lihat melakukan stale read.

  2. Simpan hasil kueri. Hal ini diperlukan karena Anda tidak dapat menulis hasil kueri kembali ke database dalam transaksi yang sama. Untuk jumlah data yang kecil, Anda dapat mencetak ke konsol atau menyimpan dalam memori. Untuk jumlah data yang lebih besar, Anda mungkin perlu menulis ke file lokal.

  3. Tulis kembali data yang dipulihkan ke tabel yang perlu dipulihkan. Misalnya:

    gcloud

    gcloud spanner rows update --instance=test-instance \
        --database=example-db --table=Singers \
        --data=SingerId=1,FirstName='Marc'

    Untuk mengetahui informasi selengkapnya, lihat memperbarui data menggunakan gcloud.

    Library klien

    Untuk mengetahui informasi selengkapnya, lihat memperbarui data menggunakan DML atau memperbarui data menggunakan mutasi.

    Jika ingin melakukan analisis pada data yang dipulihkan sebelum menulis kembali, Anda dapat membuat tabel sementara secara manual di database yang sama, menulis data yang dipulihkan ke tabel sementara ini terlebih dahulu, melakukan analisis, lalu membaca data yang ingin dipulihkan dari tabel sementara ini dan menulisnya ke tabel yang perlu dipulihkan.

Memulihkan tabel tertentu

Jika Anda perlu memulihkan tabel tertentu dan titik pemulihan yang dipilih lebih baru daripada earliest_version_time database, Anda dapat mengekspor tabel dari stempel waktu sebelumnya dalam database yang sama, lalu mengimpornya kembali.

Proses impor tidak otomatis menghapus baris yang ditambahkan setelah cadangan dibuat. Oleh karena itu, jika Anda ingin tabel yang dipulihkan sama persis dengan statusnya pada stempel waktu pemulihan yang dipilih, Anda harus menghapus baris yang dibuat setelah stempel waktu pemulihan secara manual.

  1. Ekspor tabel tertentu dari database dengan menentukan parameter snapshotTime ke stempel waktu pemulihan yang diperlukan. Anda dapat menggunakan template Dataflow Spanner ke Avro. Untuk mengetahui informasi selengkapnya, lihat Mengekspor data.
  2. Impor kembali tabel yang diekspor ke dalam database menggunakan template Dataflow Cloud Storage Avro to Spanner. Untuk mengetahui informasi selengkapnya, lihat Mengimpor data.

Memulihkan seluruh database

Anda dapat memulihkan seluruh database menggunakan Cadangkan dan Pulihkan atau Impor dan Ekspor serta menentukan stempel waktu pemulihan.

Pencadangan dan pemulihan

  1. Buat cadangan dan tetapkan version_time ke stempel waktu pemulihan yang diperlukan.

    Konsol

    1. Buka halaman Detail database di konsol Cloud.

      Buka Google Cloud konsol

    2. Di tab Backup/Restore, klik Create.

    3. Centang kotak Buat cadangan dari titik waktu sebelumnya.

      Buat formulir pencadangan di konsol dengan opsi Buat cadangan dari waktu sebelumnya dicentang dan menampilkan waktu versi paling awal.

    gcloud

    gcloud spanner backups create example-db-backup-1 \
        --instance=test-instance \
        --database=example-db \
        --retention-period=1y \
        --version-time=2021-01-22T01:10:35Z --async

    Untuk mengetahui informasi selengkapnya, lihat Membuat cadangan menggunakan gcloud.

    Library klien

    C#

    // Copyright 2020 Google Inc.
    //
    // 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.
    
    
    using Google.Cloud.Spanner.Admin.Database.V1;
    using Google.Cloud.Spanner.Common.V1;
    using Google.LongRunning;
    using Google.Protobuf.WellKnownTypes;
    using System;
    
    public class CreateBackupSample
    {
        public Backup CreateBackup(string projectId, string instanceId, string databaseId, string backupId, DateTime versionTime)
        {
            // Create the DatabaseAdminClient instance.
            DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.Create();
    
            // Initialize request parameters.
            Backup backup = new Backup
            {
                DatabaseAsDatabaseName = DatabaseName.FromProjectInstanceDatabase(projectId, instanceId, databaseId),
                ExpireTime = DateTime.UtcNow.AddDays(14).ToTimestamp(),
                VersionTime = versionTime.ToTimestamp(),
            };
            InstanceName instanceName = InstanceName.FromProjectInstance(projectId, instanceId);
    
            // Make the CreateBackup request.
            Operation<Backup, CreateBackupMetadata> response = databaseAdminClient.CreateBackup(instanceName, backup, backupId);
    
            Console.WriteLine("Waiting for the operation to finish.");
    
            // Poll until the returned long-running operation is complete.
            Operation<Backup, CreateBackupMetadata> completedResponse = response.PollUntilCompleted();
    
            if (completedResponse.IsFaulted)
            {
                Console.WriteLine($"Error while creating backup: {completedResponse.Exception}");
                throw completedResponse.Exception;
            }
    
            Console.WriteLine($"Backup created successfully.");
    
            // GetBackup to get more information about the created backup.
            BackupName backupName = BackupName.FromProjectInstanceBackup(projectId, instanceId, backupId);
            backup = databaseAdminClient.GetBackup(backupName);
            Console.WriteLine($"Backup {backup.Name} of size {backup.SizeBytes} bytes " +
                          $"was created at {backup.CreateTime} from {backup.Database} " +
                          $"and is in state {backup.State} " +
                          $"and has version time {backup.VersionTime}");
            return backup;
        }
    }
    

    C++

    void CreateBackup(google::cloud::spanner_admin::DatabaseAdminClient client,
                      std::string const& project_id, std::string const& instance_id,
                      std::string const& database_id, std::string const& backup_id,
                      google::cloud::spanner::Timestamp expire_time,
                      google::cloud::spanner::Timestamp version_time) {
      google::cloud::spanner::Database database(project_id, instance_id,
                                                database_id);
      google::spanner::admin::database::v1::CreateBackupRequest request;
      request.set_parent(database.instance().FullName());
      request.set_backup_id(backup_id);
      request.mutable_backup()->set_database(database.FullName());
      *request.mutable_backup()->mutable_expire_time() =
          expire_time.get<google::protobuf::Timestamp>().value();
      *request.mutable_backup()->mutable_version_time() =
          version_time.get<google::protobuf::Timestamp>().value();
      auto backup = client.CreateBackup(request).get();
      if (!backup) throw std::move(backup).status();
      std::cout
          << "Backup " << backup->name() << " of " << backup->database()
          << " of size " << backup->size_bytes() << " bytes as of "
          << google::cloud::spanner::MakeTimestamp(backup->version_time()).value()
          << " was created at "
          << google::cloud::spanner::MakeTimestamp(backup->create_time()).value()
          << ".\n";
    }

    Go

    
    import (
    	"context"
    	"fmt"
    	"io"
    	"regexp"
    	"time"
    
    	database "cloud.google.com/go/spanner/admin/database/apiv1"
    	adminpb "cloud.google.com/go/spanner/admin/database/apiv1/databasepb"
    	pbt "github.com/golang/protobuf/ptypes/timestamp"
    )
    
    func createBackup(ctx context.Context, w io.Writer, db, backupID string, versionTime time.Time) error {
    	// versionTime := time.Now().AddDate(0, 0, -1) // one day ago
    	matches := regexp.MustCompile("^(.+)/databases/(.+)$").FindStringSubmatch(db)
    	if matches == nil || len(matches) != 3 {
    		return fmt.Errorf("createBackup: invalid database id %q", db)
    	}
    
    	adminClient, err := database.NewDatabaseAdminClient(ctx)
    	if err != nil {
    		return fmt.Errorf("createBackup.NewDatabaseAdminClient: %w", err)
    	}
    	defer adminClient.Close()
    
    	expireTime := time.Now().AddDate(0, 0, 14)
    	// Create a backup.
    	req := adminpb.CreateBackupRequest{
    		Parent:   matches[1],
    		BackupId: backupID,
    		Backup: &adminpb.Backup{
    			Database:    db,
    			ExpireTime:  &pbt.Timestamp{Seconds: expireTime.Unix(), Nanos: int32(expireTime.Nanosecond())},
    			VersionTime: &pbt.Timestamp{Seconds: versionTime.Unix(), Nanos: int32(versionTime.Nanosecond())},
    		},
    	}
    	op, err := adminClient.CreateBackup(ctx, &req)
    	if err != nil {
    		return fmt.Errorf("createBackup.CreateBackup: %w", err)
    	}
    	// Wait for backup operation to complete.
    	backup, err := op.Wait(ctx)
    	if err != nil {
    		return fmt.Errorf("createBackup.Wait: %w", err)
    	}
    
    	// Get the name, create time, version time and backup size.
    	backupCreateTime := time.Unix(backup.CreateTime.Seconds, int64(backup.CreateTime.Nanos))
    	backupVersionTime := time.Unix(backup.VersionTime.Seconds, int64(backup.VersionTime.Nanos))
    	fmt.Fprintf(w,
    		"Backup %s of size %d bytes was created at %s with version time %s\n",
    		backup.Name,
    		backup.SizeBytes,
    		backupCreateTime.Format(time.RFC3339),
    		backupVersionTime.Format(time.RFC3339))
    	return nil
    }
    

    Java

    static void createBackup(DatabaseAdminClient dbAdminClient, String projectId, String instanceId,
        String databaseId, String backupId, Timestamp versionTime) {
      // Set expire time to 14 days from now.
      Timestamp expireTime =
          Timestamp.newBuilder().setSeconds(TimeUnit.MILLISECONDS.toSeconds((
              System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14)))).build();
      BackupName backupName = BackupName.of(projectId, instanceId, backupId);
      Backup backup = Backup.newBuilder()
          .setName(backupName.toString())
          .setDatabase(DatabaseName.of(projectId, instanceId, databaseId).toString())
          .setExpireTime(expireTime).setVersionTime(versionTime).build();
    
      // Initiate the request which returns an OperationFuture.
      System.out.println("Creating backup [" + backupId + "]...");
      try {
        // Wait for the backup operation to complete.
        backup = dbAdminClient.createBackupAsync(
            InstanceName.of(projectId, instanceId), backup, backupId).get();
        System.out.println("Created backup [" + backup.getName() + "]");
      } catch (ExecutionException e) {
        throw SpannerExceptionFactory.asSpannerException(e);
      } catch (InterruptedException e) {
        throw SpannerExceptionFactory.propagateInterrupt(e);
      }
    
      // Reload the metadata of the backup from the server.
      backup = dbAdminClient.getBackup(backup.getName());
      System.out.println(
          String.format(
              "Backup %s of size %d bytes was created at %s for version of database at %s",
              backup.getName(),
              backup.getSizeBytes(),
              java.time.OffsetDateTime.ofInstant(
                  Instant.ofEpochSecond(backup.getCreateTime().getSeconds(),
                      backup.getCreateTime().getNanos()), ZoneId.systemDefault()),
              java.time.OffsetDateTime.ofInstant(
                  Instant.ofEpochSecond(backup.getVersionTime().getSeconds(),
                      backup.getVersionTime().getNanos()), ZoneId.systemDefault()))
      );
    }

    Node.js

    
    // Imports the Google Cloud client library and precise date library
    const {Spanner, protos} = require('@google-cloud/spanner');
    const {PreciseDate} = require('@google-cloud/precise-date');
    
    /**
     * TODO(developer): Uncomment the following lines before running the sample.
     */
    // const projectId = 'my-project-id';
    // const instanceId = 'my-instance';
    // const databaseId = 'my-database';
    // const backupId = 'my-backup';
    // const versionTime = Date.now() - 1000 * 60 * 60 * 24; // One day ago
    
    // Creates a client
    const spanner = new Spanner({
      projectId: projectId,
    });
    
    // Gets a reference to a Cloud Spanner Database Admin Client object
    const databaseAdminClient