diff --git a/samples/samples/backup_sample.py b/samples/samples/backup_sample.py index e3a2b6957d..e984d3a11e 100644 --- a/samples/samples/backup_sample.py +++ b/samples/samples/backup_sample.py @@ -116,6 +116,7 @@ def create_backup_with_encryption_key( # [END spanner_create_backup_with_encryption_key] + # [START spanner_create_backup_with_MR_CMEK] def create_backup_with_multiple_kms_keys( instance_id, database_id, backup_id, kms_key_names @@ -246,6 +247,7 @@ def restore_database_with_encryption_key( # [END spanner_restore_backup_with_encryption_key] + # [START spanner_restore_backup_with_MR_CMEK] def restore_database_with_multiple_kms_keys( instance_id, new_database_id, backup_id, kms_key_names @@ -697,6 +699,7 @@ def copy_backup(instance_id, backup_id, source_backup_path): # [END spanner_copy_backup] + # [START spanner_copy_backup_with_MR_CMEK] def copy_backup_with_multiple_kms_keys( instance_id, backup_id, source_backup_path, kms_key_names diff --git a/samples/samples/backup_sample_test.py b/samples/samples/backup_sample_test.py index 5ab1e747ab..b588d5735b 100644 --- a/samples/samples/backup_sample_test.py +++ b/samples/samples/backup_sample_test.py @@ -93,8 +93,7 @@ def test_create_backup_with_encryption_key( assert kms_key_name in out -@pytest.mark.skip(reason="skipped since the KMS keys are not added on test " - "project") +@pytest.mark.skip(reason="skipped since the KMS keys are not added on test " "project") @pytest.mark.dependency(name="create_backup_with_multiple_kms_keys") def test_create_backup_with_multiple_kms_keys( capsys, @@ -116,8 +115,7 @@ def test_create_backup_with_multiple_kms_keys( assert kms_key_names[2] in out -@pytest.mark.skip(reason="skipped since the KMS keys are not added on test " - "project") +@pytest.mark.skip(reason="skipped since the KMS keys are not added on test " "project") @pytest.mark.dependency(depends=["create_backup_with_multiple_kms_keys"]) def test_copy_backup_with_multiple_kms_keys( capsys, multi_region_instance_id, spanner_client, kms_key_names @@ -164,8 +162,7 @@ def test_restore_database_with_encryption_key( assert kms_key_name in out -@pytest.mark.skip(reason="skipped since the KMS keys are not added on test " - "project") +@pytest.mark.skip(reason="skipped since the KMS keys are not added on test " "project") @pytest.mark.dependency(depends=["create_backup_with_multiple_kms_keys"]) @RetryErrors(exception=DeadlineExceeded, max_tries=2) def test_restore_database_with_multiple_kms_keys( diff --git a/samples/samples/backup_schedule_samples.py b/samples/samples/backup_schedule_samples.py index 621febf0fc..c3c86b1538 100644 --- a/samples/samples/backup_schedule_samples.py +++ b/samples/samples/backup_schedule_samples.py @@ -24,25 +24,26 @@ # [START spanner_create_full_backup_schedule] def create_full_backup_schedule( - instance_id: str, - database_id: str, - schedule_id: str, + instance_id: str, + database_id: str, + schedule_id: str, ) -> None: from datetime import timedelta from google.cloud import spanner - from google.cloud.spanner_admin_database_v1.types import \ - backup_schedule as backup_schedule_pb - from google.cloud.spanner_admin_database_v1.types import \ - CreateBackupEncryptionConfig, FullBackupSpec + from google.cloud.spanner_admin_database_v1.types import ( + backup_schedule as backup_schedule_pb, + ) + from google.cloud.spanner_admin_database_v1.types import ( + CreateBackupEncryptionConfig, + FullBackupSpec, + ) client = spanner.Client() database_admin_api = client.database_admin_api request = backup_schedule_pb.CreateBackupScheduleRequest( parent=database_admin_api.database_path( - client.project, - instance_id, - database_id + client.project, instance_id, database_id ), backup_schedule_id=schedule_id, backup_schedule=backup_schedule_pb.BackupSchedule( @@ -62,30 +63,32 @@ def create_full_backup_schedule( response = database_admin_api.create_backup_schedule(request) print(f"Created full backup schedule: {response}") + # [END spanner_create_full_backup_schedule] # [START spanner_create_incremental_backup_schedule] def create_incremental_backup_schedule( - instance_id: str, - database_id: str, - schedule_id: str, + instance_id: str, + database_id: str, + schedule_id: str, ) -> None: from datetime import timedelta from google.cloud import spanner - from google.cloud.spanner_admin_database_v1.types import \ - backup_schedule as backup_schedule_pb - from google.cloud.spanner_admin_database_v1.types import \ - CreateBackupEncryptionConfig, IncrementalBackupSpec + from google.cloud.spanner_admin_database_v1.types import ( + backup_schedule as backup_schedule_pb, + ) + from google.cloud.spanner_admin_database_v1.types import ( + CreateBackupEncryptionConfig, + IncrementalBackupSpec, + ) client = spanner.Client() database_admin_api = client.database_admin_api request = backup_schedule_pb.CreateBackupScheduleRequest( parent=database_admin_api.database_path( - client.project, - instance_id, - database_id + client.project, instance_id, database_id ), backup_schedule_id=schedule_id, backup_schedule=backup_schedule_pb.BackupSchedule( @@ -105,14 +108,16 @@ def create_incremental_backup_schedule( response = database_admin_api.create_backup_schedule(request) print(f"Created incremental backup schedule: {response}") + # [END spanner_create_incremental_backup_schedule] # [START spanner_list_backup_schedules] def list_backup_schedules(instance_id: str, database_id: str) -> None: from google.cloud import spanner - from google.cloud.spanner_admin_database_v1.types import \ - backup_schedule as backup_schedule_pb + from google.cloud.spanner_admin_database_v1.types import ( + backup_schedule as backup_schedule_pb, + ) client = spanner.Client() database_admin_api = client.database_admin_api @@ -128,18 +133,20 @@ def list_backup_schedules(instance_id: str, database_id: str) -> None: for backup_schedule in database_admin_api.list_backup_schedules(request): print(f"Backup schedule: {backup_schedule}") + # [END spanner_list_backup_schedules] # [START spanner_get_backup_schedule] def get_backup_schedule( - instance_id: str, - database_id: str, - schedule_id: str, + instance_id: str, + database_id: str, + schedule_id: str, ) -> None: from google.cloud import spanner - from google.cloud.spanner_admin_database_v1.types import \ - backup_schedule as backup_schedule_pb + from google.cloud.spanner_admin_database_v1.types import ( + backup_schedule as backup_schedule_pb, + ) client = spanner.Client() database_admin_api = client.database_admin_api @@ -156,21 +163,24 @@ def get_backup_schedule( response = database_admin_api.get_backup_schedule(request) print(f"Backup schedule: {response}") + # [END spanner_get_backup_schedule] # [START spanner_update_backup_schedule] def update_backup_schedule( - instance_id: str, - database_id: str, - schedule_id: str, + instance_id: str, + database_id: str, + schedule_id: str, ) -> None: from datetime import timedelta from google.cloud import spanner - from google.cloud.spanner_admin_database_v1.types import \ - backup_schedule as backup_schedule_pb - from google.cloud.spanner_admin_database_v1.types import \ - CreateBackupEncryptionConfig + from google.cloud.spanner_admin_database_v1.types import ( + backup_schedule as backup_schedule_pb, + ) + from google.cloud.spanner_admin_database_v1.types import ( + CreateBackupEncryptionConfig, + ) from google.protobuf.field_mask_pb2 import FieldMask client = spanner.Client() @@ -206,18 +216,20 @@ def update_backup_schedule( response = database_admin_api.update_backup_schedule(request) print(f"Updated backup schedule: {response}") + # [END spanner_update_backup_schedule] # [START spanner_delete_backup_schedule] def delete_backup_schedule( - instance_id: str, - database_id: str, - schedule_id: str, + instance_id: str, + database_id: str, + schedule_id: str, ) -> None: from google.cloud import spanner - from google.cloud.spanner_admin_database_v1.types import \ - backup_schedule as backup_schedule_pb + from google.cloud.spanner_admin_database_v1.types import ( + backup_schedule as backup_schedule_pb, + ) client = spanner.Client() database_admin_api = client.database_admin_api @@ -234,6 +246,7 @@ def delete_backup_schedule( database_admin_api.delete_backup_schedule(request) print("Deleted backup schedule") + # [END spanner_delete_backup_schedule] diff --git a/samples/samples/backup_schedule_samples_test.py b/samples/samples/backup_schedule_samples_test.py index eb4be96b43..6584d89701 100644 --- a/samples/samples/backup_schedule_samples_test.py +++ b/samples/samples/backup_schedule_samples_test.py @@ -33,9 +33,9 @@ def database_id(): @pytest.mark.dependency(name="create_full_backup_schedule") def test_create_full_backup_schedule( - capsys, - sample_instance, - sample_database, + capsys, + sample_instance, + sample_database, ) -> None: samples.create_full_backup_schedule( sample_instance.instance_id, @@ -53,9 +53,9 @@ def test_create_full_backup_schedule( @pytest.mark.dependency(name="create_incremental_backup_schedule") def test_create_incremental_backup_schedule( - capsys, - sample_instance, - sample_database, + capsys, + sample_instance, + sample_database, ) -> None: samples.create_incremental_backup_schedule( sample_instance.instance_id, @@ -71,14 +71,16 @@ def test_create_incremental_backup_schedule( ) in out -@pytest.mark.dependency(depends=[ - "create_full_backup_schedule", - "create_incremental_backup_schedule", -]) +@pytest.mark.dependency( + depends=[ + "create_full_backup_schedule", + "create_incremental_backup_schedule", + ] +) def test_list_backup_schedules( - capsys, - sample_instance, - sample_database, + capsys, + sample_instance, + sample_database, ) -> None: samples.list_backup_schedules( sample_instance.instance_id, @@ -99,9 +101,9 @@ def test_list_backup_schedules( @pytest.mark.dependency(depends=["create_full_backup_schedule"]) def test_get_backup_schedule( - capsys, - sample_instance, - sample_database, + capsys, + sample_instance, + sample_database, ) -> None: samples.get_backup_schedule( sample_instance.instance_id, @@ -118,9 +120,9 @@ def test_get_backup_schedule( @pytest.mark.dependency(depends=["create_full_backup_schedule"]) def test_update_backup_schedule( - capsys, - sample_instance, - sample_database, + capsys, + sample_instance, + sample_database, ) -> None: samples.update_backup_schedule( sample_instance.instance_id, @@ -136,14 +138,16 @@ def test_update_backup_schedule( ) in out -@pytest.mark.dependency(depends=[ - "create_full_backup_schedule", - "create_incremental_backup_schedule", -]) +@pytest.mark.dependency( + depends=[ + "create_full_backup_schedule", + "create_incremental_backup_schedule", + ] +) def test_delete_backup_schedule( - capsys, - sample_instance, - sample_database, + capsys, + sample_instance, + sample_database, ) -> None: samples.delete_backup_schedule( sample_instance.instance_id, diff --git a/samples/samples/pg_snippets.py b/samples/samples/pg_snippets.py index ad8744794a..432d68a8ce 100644 --- a/samples/samples/pg_snippets.py +++ b/samples/samples/pg_snippets.py @@ -69,8 +69,7 @@ def create_instance(instance_id): def create_database(instance_id, database_id): """Creates a PostgreSql database and tables for sample data.""" - from google.cloud.spanner_admin_database_v1.types import \ - spanner_database_admin + from google.cloud.spanner_admin_database_v1.types import spanner_database_admin spanner_client = spanner.Client() database_admin_api = spanner_client.database_admin_api @@ -91,8 +90,7 @@ def create_database(instance_id, database_id): def create_table_using_ddl(database_name): - from google.cloud.spanner_admin_database_v1.types import \ - spanner_database_admin + from google.cloud.spanner_admin_database_v1.types import spanner_database_admin spanner_client = spanner.Client() request = spanner_database_admin.UpdateDatabaseDdlRequest( @@ -240,8 +238,7 @@ def read_data(instance_id, database_id): def add_column(instance_id, database_id): """Adds a new column to the Albums table in the example database.""" - from google.cloud.spanner_admin_database_v1.types import \ - spanner_database_admin + from google.cloud.spanner_admin_database_v1.types import spanner_database_admin spanner_client = spanner.Client() database_admin_api = spanner_client.database_admin_api @@ -441,8 +438,7 @@ def read_data_with_index(instance_id, database_id): def add_storing_index(instance_id, database_id): """Adds an storing index to the example database.""" - from google.cloud.spanner_admin_database_v1.types import \ - spanner_database_admin + from google.cloud.spanner_admin_database_v1.types import spanner_database_admin spanner_client = spanner.Client() database_admin_api = spanner_client.database_admin_api @@ -1091,8 +1087,7 @@ def create_table_with_datatypes(instance_id, database_id): # instance_id = "your-spanner-instance" # database_id = "your-spanner-db-id" - from google.cloud.spanner_admin_database_v1.types import \ - spanner_database_admin + from google.cloud.spanner_admin_database_v1.types import spanner_database_admin spanner_client = spanner.Client() database_admin_api = spanner_client.database_admin_api @@ -1476,8 +1471,7 @@ def add_jsonb_column(instance_id, database_id): # instance_id = "your-spanner-instance" # database_id = "your-spanner-db-id" - from google.cloud.spanner_admin_database_v1.types import \ - spanner_database_admin + from google.cloud.spanner_admin_database_v1.types import spanner_database_admin spanner_client = spanner.Client() database_admin_api = spanner_client.database_admin_api @@ -1593,8 +1587,7 @@ def query_data_with_jsonb_parameter(instance_id, database_id): def create_sequence(instance_id, database_id): """Creates the Sequence and insert data""" - from google.cloud.spanner_admin_database_v1.types import \ - spanner_database_admin + from google.cloud.spanner_admin_database_v1.types import spanner_database_admin spanner_client = spanner.Client() database_admin_api = spanner_client.database_admin_api @@ -1651,8 +1644,7 @@ def insert_customers(transaction): def alter_sequence(instance_id, database_id): """Alters the Sequence and insert data""" - from google.cloud.spanner_admin_database_v1.types import \ - spanner_database_admin + from google.cloud.spanner_admin_database_v1.types import spanner_database_admin spanner_client = spanner.Client() database_admin_api = spanner_client.database_admin_api @@ -1703,8 +1695,7 @@ def insert_customers(transaction): def drop_sequence(instance_id, database_id): """Drops the Sequence""" - from google.cloud.spanner_admin_database_v1.types import \ - spanner_database_admin + from google.cloud.spanner_admin_database_v1.types import spanner_database_admin spanner_client = spanner.Client() database_admin_api = spanner_client.database_admin_api diff --git a/samples/samples/snippets.py b/samples/samples/snippets.py index 87b7ab86a2..96d8fd3f89 100644 --- a/samples/samples/snippets.py +++ b/samples/samples/snippets.py @@ -75,11 +75,11 @@ def create_instance(instance_id): # [END spanner_create_instance] + # [START spanner_update_instance] def update_instance(instance_id): """Updates an instance.""" - from google.cloud.spanner_admin_instance_v1.types import \ - spanner_instance_admin + from google.cloud.spanner_admin_instance_v1.types import spanner_instance_admin spanner_client = spanner.Client() @@ -366,6 +366,7 @@ def create_database_with_encryption_key(instance_id, database_id, kms_key_name): # [END spanner_create_database_with_encryption_key] + # [START spanner_create_database_with_MR_CMEK] def create_database_with_multiple_kms_keys(instance_id, database_id, kms_key_names): """Creates a database with tables using multiple KMS keys(CMEK).""" @@ -409,6 +410,7 @@ def create_database_with_multiple_kms_keys(instance_id, database_id, kms_key_nam # [END spanner_create_database_with_MR_CMEK] + # [START spanner_create_database_with_default_leader] def create_database_with_default_leader(instance_id, database_id, default_leader): """Creates a database with tables with a default leader.""" @@ -1591,7 +1593,11 @@ def __init__(self): super().__init__("commit_stats_sample") def info(self, msg, *args, **kwargs): - if "extra" in kwargs and kwargs["extra"] and "commit_stats" in kwargs["extra"]: + if ( + "extra" in kwargs + and kwargs["extra"] + and "commit_stats" in kwargs["extra"] + ): self.last_commit_stats = kwargs["extra"]["commit_stats"] super().info(msg, *args, **kwargs) @@ -3176,6 +3182,56 @@ def directed_read_options( # [END spanner_directed_read] +def isolation_level_options( + instance_id, + database_id, +): + from google.cloud.spanner_v1 import TransactionOptions, DefaultTransactionOptions + + """ + Shows how to run a Read Write transaction with isolation level options. + """ + # [START spanner_isolation_level] + # instance_id = "your-spanner-instance" + # database_id = "your-spanner-db-id" + + # The isolation level specified at the client-level will be applied to all RW transactions. + isolation_options_for_client = TransactionOptions.IsolationLevel.SERIALIZABLE + + spanner_client = spanner.Client( + default_transaction_options=DefaultTransactionOptions( + isolation_level=isolation_options_for_client + ) + ) + instance = spanner_client.instance(instance_id) + database = instance.database(database_id) + + # The isolation level specified at the request level takes precedence over the isolation level configured at the client level. + isolation_options_for_transaction = ( + TransactionOptions.IsolationLevel.REPEATABLE_READ + ) + + def update_albums_with_isolation(transaction): + # Read an AlbumTitle. + results = transaction.execute_sql( + "SELECT AlbumTitle from Albums WHERE SingerId = 1 and AlbumId = 1" + ) + for result in results: + print("Current Album Title: {}".format(*result)) + + # Update the AlbumTitle. + row_ct = transaction.execute_update( + "UPDATE Albums SET AlbumTitle = 'A New Title' WHERE SingerId = 1 and AlbumId = 1" + ) + + print("{} record(s) updated.".format(row_ct)) + + database.run_in_transaction( + update_albums_with_isolation, isolation_level=isolation_options_for_transaction + ) + # [END spanner_isolation_level] + + def set_custom_timeout_and_retry(instance_id, database_id): """Executes a snapshot read with custom timeout and retry.""" # [START spanner_set_custom_timeout_and_retry] @@ -3288,14 +3344,14 @@ def create_instance_without_default_backup_schedules(instance_id): ) operation = spanner_client.instance_admin_api.create_instance( - parent=spanner_client.project_name, - instance_id=instance_id, - instance=spanner_instance_admin.Instance( - config=config_name, - display_name="This is a display name.", - node_count=1, - default_backup_schedule_type=spanner_instance_admin.Instance.DefaultBackupScheduleType.NONE, # Optional - ), + parent=spanner_client.project_name, + instance_id=instance_id, + instance=spanner_instance_admin.Instance( + config=config_name, + display_name="This is a display name.", + node_count=1, + default_backup_schedule_type=spanner_instance_admin.Instance.DefaultBackupScheduleType.NONE, # Optional + ), ) print("Waiting for operation to complete...") @@ -3314,13 +3370,11 @@ def update_instance_default_backup_schedule_type(instance_id): name = "{}/instances/{}".format(spanner_client.project_name, instance_id) operation = spanner_client.instance_admin_api.update_instance( - instance=spanner_instance_admin.Instance( - name=name, - default_backup_schedule_type=spanner_instance_admin.Instance.DefaultBackupScheduleType.AUTOMATIC, # Optional - ), - field_mask=field_mask_pb2.FieldMask( - paths=["default_backup_schedule_type"] - ), + instance=spanner_instance_admin.Instance( + name=name, + default_backup_schedule_type=spanner_instance_admin.Instance.DefaultBackupScheduleType.AUTOMATIC, # Optional + ), + field_mask=field_mask_pb2.FieldMask(paths=["default_backup_schedule_type"]), ) print("Waiting for operation to complete...") @@ -3581,7 +3635,9 @@ def add_split_points(instance_id, database_id): database=database_admin_api.database_path( spanner_client.project, instance_id, database_id ), - statements=["CREATE INDEX IF NOT EXISTS SingersByFirstLastName ON Singers(FirstName, LastName)"], + statements=[ + "CREATE INDEX IF NOT EXISTS SingersByFirstLastName ON Singers(FirstName, LastName)" + ], ) operation = database_admin_api.update_database_ddl(request) @@ -3638,7 +3694,6 @@ def add_split_points(instance_id, database_id): values=[struct_pb2.Value(string_value="38")] ) ), - ], ), ], @@ -3798,6 +3853,9 @@ def add_split_points(instance_id, database_id): ) enable_fine_grained_access_parser.add_argument("--title", default="condition title") subparsers.add_parser("directed_read_options", help=directed_read_options.__doc__) + subparsers.add_parser( + "isolation_level_options", help=isolation_level_options.__doc__ + ) subparsers.add_parser( "set_custom_timeout_and_retry", help=set_custom_timeout_and_retry.__doc__ ) @@ -3958,6 +4016,8 @@ def add_split_points(instance_id, database_id): ) elif args.command == "directed_read_options": directed_read_options(args.instance_id, args.database_id) + elif args.command == "isolation_level_options": + isolation_level_options(args.instance_id, args.database_id) elif args.command == "set_custom_timeout_and_retry": set_custom_timeout_and_retry(args.instance_id, args.database_id) elif args.command == "create_instance_with_autoscaling_config": diff --git a/samples/samples/snippets_test.py b/samples/samples/snippets_test.py index 72f243fdb5..03c9f2682c 100644 --- a/samples/samples/snippets_test.py +++ b/samples/samples/snippets_test.py @@ -197,7 +197,9 @@ def test_create_instance_with_autoscaling_config(capsys, lci_instance_id): retry_429(instance.delete)() -def test_create_and_update_instance_default_backup_schedule_type(capsys, lci_instance_id): +def test_create_and_update_instance_default_backup_schedule_type( + capsys, lci_instance_id +): retry_429(snippets.create_instance_without_default_backup_schedules)( lci_instance_id, ) @@ -252,8 +254,7 @@ def test_create_database_with_encryption_config( assert kms_key_name in out -@pytest.mark.skip(reason="skipped since the KMS keys are not added on test " - "project") +@pytest.mark.skip(reason="skipped since the KMS keys are not added on test " "project") def test_create_database_with_multiple_kms_keys( capsys, multi_region_instance, @@ -991,6 +992,13 @@ def test_set_custom_timeout_and_retry(capsys, instance_id, sample_database): assert "SingerId: 1, AlbumId: 1, AlbumTitle: Total Junk" in out +@pytest.mark.dependency(depends=["insert_data"]) +def test_isolated_level_options(capsys, instance_id, sample_database): + snippets.isolation_level_options(instance_id, sample_database.database_id) + out, _ = capsys.readouterr() + assert "1 record(s) updated." in out + + @pytest.mark.dependency( name="add_proto_types_column", )