From d82d74313b27405e2a0a59cb77d539ee5000f5ab Mon Sep 17 00:00:00 2001 From: Zoe Cai Date: Tue, 6 Jul 2021 14:26:41 +1000 Subject: [PATCH 01/17] feat: add configurable leader placement support --- google/cloud/spanner_v1/database.py | 11 +++++ tests/system/test_system.py | 71 +++++++++++++++++++++++++++++ tests/unit/test_client.py | 6 ++- tests/unit/test_database.py | 10 ++++ 4 files changed, 97 insertions(+), 1 deletion(-) diff --git a/google/cloud/spanner_v1/database.py b/google/cloud/spanner_v1/database.py index fae983f334..3d62737e03 100644 --- a/google/cloud/spanner_v1/database.py +++ b/google/cloud/spanner_v1/database.py @@ -144,6 +144,7 @@ def __init__( self._version_retention_period = None self._earliest_version_time = None self._encryption_info = None + self._default_leader = None self.log_commit_stats = False self._logger = logger self._encryption_config = encryption_config @@ -279,6 +280,15 @@ def encryption_info(self): """ return self._encryption_info + @property + def default_leader(self): + """The read-write region which contains the database's leader replicas. + + :rtype: str + :returns: a string representing the read-write region + """ + return self._default_leader + @property def ddl_statements(self): """DDL Statements used to define database schema. @@ -414,6 +424,7 @@ def reload(self): self._earliest_version_time = response.earliest_version_time self._encryption_config = response.encryption_config self._encryption_info = response.encryption_info + self._default_leader = response.default_leader def update_ddl(self, ddl_statements, operation_id=""): """Update DDL for this database. diff --git a/tests/system/test_system.py b/tests/system/test_system.py index ad2b8a9178..bab9eaf447 100644 --- a/tests/system/test_system.py +++ b/tests/system/test_system.py @@ -443,6 +443,46 @@ def test_create_database_pitr_success(self): for result in results: self.assertEqual(result[0], retention_period) + @unittest.skipIf( + USE_EMULATOR, "Default leader setting is not supported by the emulator" + ) + def test_create_database_with_default_leader_success(self): + pool = BurstyPool(labels={"testcase": "create_database_default_leader"}) + temp_db_id = "temp_db" + unique_resource_id("_") + default_leader = "us-east4" + ddl_statements = [ + "ALTER DATABASE {}" + " SET OPTIONS (default_leader = '{}')".format( + temp_db_id, default_leader + ) + ] + temp_db = Config.INSTANCE.database( + temp_db_id, pool=pool, ddl_statements=ddl_statements + ) + operation = temp_db.create() + self.to_delete.append(temp_db) + + # We want to make sure the operation completes. + operation.result(30) # raises on failure / timeout. + + database_ids = [database.name for database in Config.INSTANCE.list_databases()] + self.assertIn(temp_db.name, database_ids) + + temp_db.reload() + self.assertEqual(temp_db.default_reader, default_leader) + + with self.assertRaises(exceptions.InvalidArgument): + temp_db.create() + + with temp_db.snapshot() as snapshot: + results = snapshot.execute_sql( + "SELECT OPTION_VALUE AS default_leader " + "FROM INFORMATION_SCHEMA.DATABASE_OPTIONS " + "WHERE SCHEMA_NAME = '' AND OPTION_NAME = 'default_leader'" + ) + for result in results: + self.assertEqual(result[0], default_leader) + def test_table_not_found(self): temp_db_id = "temp_db" + unique_resource_id("_") @@ -551,6 +591,37 @@ def test_update_database_ddl_pitr_success(self): self.assertEqual(temp_db.version_retention_period, retention_period) self.assertEqual(len(temp_db.ddl_statements), len(ddl_statements)) + @unittest.skipIf( + USE_EMULATOR, "Default leader update is not supported by the emulator" + ) + def test_update_database_ddl_default_leader_success(self): + pool = BurstyPool(labels={"testcase": "update_database_ddl_default_leader"}) + temp_db_id = "temp_db" + unique_resource_id("_") + default_leader = "us-east4" + temp_db = Config.INSTANCE.database(temp_db_id, pool=pool) + create_op = temp_db.create() + self.to_delete.append(temp_db) + + # We want to make sure the operation completes. + create_op.result(240) # raises on failure / timeout. + + self.assertIsNone(temp_db.default_leader) + + ddl_statements = DDL_STATEMENTS + [ + "ALTER DATABASE {}" + " SET OPTIONS (default_leader = '{}')".format( + temp_db_id, default_leader + ) + ] + operation = temp_db.update_ddl(ddl_statements) + + # We want to make sure the operation completes. + operation.result(240) # raises on failure / timeout. + + temp_db.reload() + self.assertEqual(temp_db.default_leader, default_leader) + self.assertEqual(len(temp_db.ddl_statements), len(ddl_statements)) + def test_db_batch_insert_then_db_snapshot_read(self): retry = RetryInstanceState(_has_all_ddl) retry(self._db.reload)() diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index 2777fbc9a0..f3a744a96c 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -40,6 +40,7 @@ class TestClient(unittest.TestCase): PROCESSING_UNITS = 5000 LABELS = {"test": "true"} TIMEOUT_SECONDS = 80 + LEADER_OPTIONS = ["leader1", "leader2"] def _get_target_class(self): from google.cloud import spanner @@ -457,7 +458,9 @@ def test_list_instance_configs(self): instance_config_pbs = ListInstanceConfigsResponse( instance_configs=[ InstanceConfigPB( - name=self.CONFIGURATION_NAME, display_name=self.DISPLAY_NAME + name=self.CONFIGURATION_NAME, + display_name=self.DISPLAY_NAME, + leader_options=self.LEADER_OPTIONS ) ] ) @@ -473,6 +476,7 @@ def test_list_instance_configs(self): self.assertIsInstance(instance_config, InstanceConfigPB) self.assertEqual(instance_config.name, self.CONFIGURATION_NAME) self.assertEqual(instance_config.display_name, self.DISPLAY_NAME) + self.assertEqual(instance_config.leader_options, self.LEADER_OPTIONS) expected_metadata = ( ("google-cloud-resource-prefix", client.project_name), diff --git a/tests/unit/test_database.py b/tests/unit/test_database.py index 05e6f2b422..a4b7aa2425 100644 --- a/tests/unit/test_database.py +++ b/tests/unit/test_database.py @@ -333,6 +333,13 @@ def test_encryption_info(self): ] self.assertEqual(database.encryption_info, encryption_info) + def test_default_leader(self): + instance = _Instance(self.INSTANCE_NAME) + pool = _Pool() + database = self._make_one(self.DATABASE_ID, instance, pool=pool) + default_leader = database._default_leader = "us-east4" + self.assertEqual(database.default_leader, default_leader) + def test_spanner_api_property_w_scopeless_creds(self): client = _Client() @@ -715,6 +722,7 @@ def test_reload_success(self): kms_key_version="kms_key_version", ) ] + default_leader = "us-east4" api = client.database_admin_api = self._make_database_admin_api() api.get_database_ddl.return_value = ddl_pb db_pb = Database( @@ -725,6 +733,7 @@ def test_reload_success(self): earliest_version_time=_datetime_to_pb_timestamp(timestamp), encryption_config=encryption_config, encryption_info=encryption_info, + default_leader=default_leader, ) api.get_database.return_value = db_pb instance = _Instance(self.INSTANCE_NAME, client=client) @@ -740,6 +749,7 @@ def test_reload_success(self): self.assertEqual(database._ddl_statements, tuple(DDL_STATEMENTS)) self.assertEqual(database._encryption_config, encryption_config) self.assertEqual(database._encryption_info, encryption_info) + self.assertEqual(database._default_leader, default_leader) api.get_database_ddl.assert_called_once_with( database=self.DATABASE_NAME, From 35d8e3b0d40532323f0062a5c90c9dd1c514ca29 Mon Sep 17 00:00:00 2001 From: Zoe Cai Date: Wed, 7 Jul 2021 10:48:43 +1000 Subject: [PATCH 02/17] lint --- tests/system/test_system.py | 8 ++------ tests/unit/test_client.py | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/system/test_system.py b/tests/system/test_system.py index bab9eaf447..547629f361 100644 --- a/tests/system/test_system.py +++ b/tests/system/test_system.py @@ -452,9 +452,7 @@ def test_create_database_with_default_leader_success(self): default_leader = "us-east4" ddl_statements = [ "ALTER DATABASE {}" - " SET OPTIONS (default_leader = '{}')".format( - temp_db_id, default_leader - ) + " SET OPTIONS (default_leader = '{}')".format(temp_db_id, default_leader) ] temp_db = Config.INSTANCE.database( temp_db_id, pool=pool, ddl_statements=ddl_statements @@ -609,9 +607,7 @@ def test_update_database_ddl_default_leader_success(self): ddl_statements = DDL_STATEMENTS + [ "ALTER DATABASE {}" - " SET OPTIONS (default_leader = '{}')".format( - temp_db_id, default_leader - ) + " SET OPTIONS (default_leader = '{}')".format(temp_db_id, default_leader) ] operation = temp_db.update_ddl(ddl_statements) diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index f3a744a96c..68d8ea6857 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -460,7 +460,7 @@ def test_list_instance_configs(self): InstanceConfigPB( name=self.CONFIGURATION_NAME, display_name=self.DISPLAY_NAME, - leader_options=self.LEADER_OPTIONS + leader_options=self.LEADER_OPTIONS, ) ] ) From da35d3391df94f846cd35866593946812643d498 Mon Sep 17 00:00:00 2001 From: Zoe Cai Date: Thu, 22 Jul 2021 21:20:49 +1000 Subject: [PATCH 03/17] Create multi-regional instances --- tests/system/test_system.py | 39 +++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/tests/system/test_system.py b/tests/system/test_system.py index 547629f361..d72d42e0f9 100644 --- a/tests/system/test_system.py +++ b/tests/system/test_system.py @@ -358,11 +358,14 @@ def tearDownClass(cls): cls._db.drop() def setUp(self): + self.instances_to_delete = [] self.to_delete = [] def tearDown(self): for doomed in self.to_delete: doomed.drop() + for instance in self.instances_to_delete: + instance.delete() def test_list_databases(self): # Since `Config.INSTANCE` is newly created in `setUpModule`, the @@ -448,13 +451,28 @@ def test_create_database_pitr_success(self): ) def test_create_database_with_default_leader_success(self): pool = BurstyPool(labels={"testcase": "create_database_default_leader"}) + + # Create a multi-region instance + ALT_INSTANCE_ID = "new" + unique_resource_id("-") + config_name = "{}/instanceConfigs/cloud-devel-global-config".format( + Config.CLIENT.project_name + ) + instance = Config.CLIENT.instance( + instance_id=ALT_INSTANCE_ID, configuration_name=config_name + ) + operation = instance.create() + self.instances_to_delete.append(instance) + operation.result(SPANNER_OPERATION_TIMEOUT_IN_SECONDS) + + print(Config.CLIENT.list_instance_configs) + temp_db_id = "temp_db" + unique_resource_id("_") default_leader = "us-east4" ddl_statements = [ "ALTER DATABASE {}" " SET OPTIONS (default_leader = '{}')".format(temp_db_id, default_leader) ] - temp_db = Config.INSTANCE.database( + temp_db = instance.database( temp_db_id, pool=pool, ddl_statements=ddl_statements ) operation = temp_db.create() @@ -463,11 +481,11 @@ def test_create_database_with_default_leader_success(self): # We want to make sure the operation completes. operation.result(30) # raises on failure / timeout. - database_ids = [database.name for database in Config.INSTANCE.list_databases()] + database_ids = [database.name for database in instance.list_databases()] self.assertIn(temp_db.name, database_ids) temp_db.reload() - self.assertEqual(temp_db.default_reader, default_leader) + self.assertEqual(temp_db.default_leader, default_leader) with self.assertRaises(exceptions.InvalidArgument): temp_db.create() @@ -594,9 +612,22 @@ def test_update_database_ddl_pitr_success(self): ) def test_update_database_ddl_default_leader_success(self): pool = BurstyPool(labels={"testcase": "update_database_ddl_default_leader"}) + + # Create a multi-region instance + ALT_INSTANCE_ID = "new" + unique_resource_id("-") + config_name = "{}/instanceConfigs/cloud-devel-global-config".format( + Config.CLIENT.project_name + ) + instance = Config.CLIENT.instance( + instance_id=ALT_INSTANCE_ID, configuration_name=config_name + ) + operation = instance.create() + self.instances_to_delete.append(instance) + operation.result(SPANNER_OPERATION_TIMEOUT_IN_SECONDS) + temp_db_id = "temp_db" + unique_resource_id("_") default_leader = "us-east4" - temp_db = Config.INSTANCE.database(temp_db_id, pool=pool) + temp_db = instance.database(temp_db_id, pool=pool) create_op = temp_db.create() self.to_delete.append(temp_db) From 8f73d2c14b50d924aa231aa7a7006f0cc7ca427a Mon Sep 17 00:00:00 2001 From: Zoe Cai Date: Thu, 22 Jul 2021 22:22:32 +1000 Subject: [PATCH 04/17] try another print stmt --- tests/system/test_system.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/system/test_system.py b/tests/system/test_system.py index d72d42e0f9..bbfd1908f4 100644 --- a/tests/system/test_system.py +++ b/tests/system/test_system.py @@ -117,6 +117,8 @@ def setUpModule(): retry = RetryErrors(exceptions.ServiceUnavailable) configs = list(retry(Config.CLIENT.list_instance_configs)()) + print("zoe configs") + print(configs) instances = retry(_list_instances)() EXISTING_INSTANCES[:] = instances @@ -464,8 +466,6 @@ def test_create_database_with_default_leader_success(self): self.instances_to_delete.append(instance) operation.result(SPANNER_OPERATION_TIMEOUT_IN_SECONDS) - print(Config.CLIENT.list_instance_configs) - temp_db_id = "temp_db" + unique_resource_id("_") default_leader = "us-east4" ddl_statements = [ From 6bb7b0bea71dc2c45bd0ed5a437621d7e5d7cf48 Mon Sep 17 00:00:00 2001 From: Zoe Cai Date: Thu, 22 Jul 2021 23:38:49 +1000 Subject: [PATCH 05/17] try nam3 --- tests/system/test_system.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/system/test_system.py b/tests/system/test_system.py index bbfd1908f4..0e5dc05121 100644 --- a/tests/system/test_system.py +++ b/tests/system/test_system.py @@ -117,8 +117,6 @@ def setUpModule(): retry = RetryErrors(exceptions.ServiceUnavailable) configs = list(retry(Config.CLIENT.list_instance_configs)()) - print("zoe configs") - print(configs) instances = retry(_list_instances)() EXISTING_INSTANCES[:] = instances @@ -456,7 +454,7 @@ def test_create_database_with_default_leader_success(self): # Create a multi-region instance ALT_INSTANCE_ID = "new" + unique_resource_id("-") - config_name = "{}/instanceConfigs/cloud-devel-global-config".format( + config_name = "{}/instanceConfigs/nam3".format( Config.CLIENT.project_name ) instance = Config.CLIENT.instance( @@ -615,7 +613,7 @@ def test_update_database_ddl_default_leader_success(self): # Create a multi-region instance ALT_INSTANCE_ID = "new" + unique_resource_id("-") - config_name = "{}/instanceConfigs/cloud-devel-global-config".format( + config_name = "{}/instanceConfigs/nam3".format( Config.CLIENT.project_name ) instance = Config.CLIENT.instance( From e939fa7fbf5472f117f7150c5bd54fb0ccdc9810 Mon Sep 17 00:00:00 2001 From: Zoe Cai Date: Fri, 23 Jul 2021 15:27:24 +1000 Subject: [PATCH 06/17] variable for config --- tests/system/test_system.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/system/test_system.py b/tests/system/test_system.py index 0e5dc05121..311c76d849 100644 --- a/tests/system/test_system.py +++ b/tests/system/test_system.py @@ -453,9 +453,10 @@ def test_create_database_with_default_leader_success(self): pool = BurstyPool(labels={"testcase": "create_database_default_leader"}) # Create a multi-region instance + multi_region_config = "nam3" ALT_INSTANCE_ID = "new" + unique_resource_id("-") - config_name = "{}/instanceConfigs/nam3".format( - Config.CLIENT.project_name + config_name = "{}/instanceConfigs/{}".format( + Config.CLIENT.project_name, multi_region_config ) instance = Config.CLIENT.instance( instance_id=ALT_INSTANCE_ID, configuration_name=config_name @@ -612,9 +613,10 @@ def test_update_database_ddl_default_leader_success(self): pool = BurstyPool(labels={"testcase": "update_database_ddl_default_leader"}) # Create a multi-region instance + multi_region_config = "nam3" ALT_INSTANCE_ID = "new" + unique_resource_id("-") - config_name = "{}/instanceConfigs/nam3".format( - Config.CLIENT.project_name + config_name = "{}/instanceConfigs/{}".format( + Config.CLIENT.project_name, multi_region_config ) instance = Config.CLIENT.instance( instance_id=ALT_INSTANCE_ID, configuration_name=config_name From 3ce1ed75c50825598fa01529761e6882e607171c Mon Sep 17 00:00:00 2001 From: Zoe Cai Date: Mon, 26 Jul 2021 22:37:10 +1000 Subject: [PATCH 07/17] samples: add default leader options samples --- samples/samples/snippets.py | 178 +++++++++++++++++++++++++++++++ samples/samples/snippets_test.py | 80 +++++++++++++- 2 files changed, 256 insertions(+), 2 deletions(-) diff --git a/samples/samples/snippets.py b/samples/samples/snippets.py index c6c3972e32..3cf75c46eb 100644 --- a/samples/samples/snippets.py +++ b/samples/samples/snippets.py @@ -96,6 +96,46 @@ def create_instance_with_processing_units(instance_id, processing_units): # [END spanner_create_instance_with_processing_units] +# [START spanner_get_instance_config] +def get_instance_config(instance_config): + """Gets the leader options for the instance configuration.""" + from google.cloud.spanner_admin_instance_v1 import GetInstanceConfigRequest + spanner_client = spanner.Client() + config = spanner_client.instance_admin_api.get_instance_config(name=instance_config) + print("Available leader options for instance config {}: {}".format( + instance_config, config.leader_options)) + + +# [END spanner_get_instance_config] + + +# [START spanner_list_instance_configs] +def list_instance_config(): + """Lists the available instance configurations.""" + spanner_client = spanner.Client() + configs = spanner_client.list_instance_configs() + print("Available instance configs: {}".format(configs)) + + +# [END spanner_list_instance_configs] + + +# [START spanner_list_databases] +def list_databases(instance_id): + """Lists databases and their leader options.""" + spanner_client = spanner.Client() + instance = spanner_client.instance(instance_id) + + databases = list(instance.list_databases()) + default_leaders = [database.default_leader for database in databases] + + print("Databases: {}".format(databases)) + print("Default leaders of databases: {}".format(default_leaders)) + + +# [END spanner_list_databases] + + # [START spanner_create_database] def create_database(instance_id, database_id): """Creates a database and tables for sample data.""" @@ -168,6 +208,144 @@ 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_default_leader] +def create_database_with_default_leader( + instance_id, database_id, configRegion, default_leader +): + """Creates a database with tables with a default leader.""" + spanner_client = spanner.Client() + + config_name = "{}/instanceConfigs/{}".format(spanner_client.project_name, configRegion) + instance = spanner_client.instance( + instance_id, configuration_name=config_name + ) + operation = instance.create() + + print("Waiting for operation to complete...") + operation.result(120) + + database = instance.database( + database_id, + 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 (default_leader = '{}')".format(database_id, default_leader), + ], + ) + operation = database.create() + + print("Waiting for operation to complete...") + operation.result(120) + + print( + "Database {} created with default leader {}".format( + database.name, database.default_leader + ) + ) + + +# [END spanner_create_database_with_default_leader] + + +# [START spanner_update_database_with_default_leader] +def update_database_with_default_leader( + instance_id, database_id, configRegion, default_leader +): + """Updates a database with tables with a default leader.""" + spanner_client = spanner.Client() + + config_name = "{}/instanceConfigs/{}".format(spanner_client.project_name, configRegion) + instance = spanner_client.instance( + instance_id, configuration_name=config_name + ) + operation = instance.create() + + print("Waiting for operation to complete...") + operation.result(120) + + database = instance.database( + database_id, + 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""", + ], + ) + operation = database.create() + + print("Waiting for operation to complete...") + operation.result(120) + + database.update_ddl(["ALTER DATABASE {}" + " SET OPTIONS (default_leader = '{}')".format(database_id, default_leader)]) + operation.result(120) + + print( + "Database {} updated with default leader {}".format( + database.name, database.default_leader + ) + ) + + +# [END spanner_update_database_with_default_leader] + + +# [START spanner_get_database_ddl] +def get_database_ddl(instance_id, database_id): + """Gets the database DDL statements.""" + spanner_client = spanner.Client() + instance = spanner_client.instance(instance_id) + database = instance.database(database_id) + ddl = spanner_client.database_admin_api.get_database_ddl(database.name) + print("Retrieved database DDL for {}".format(database_id)) + for statement in ddl.statements: + print(statement) + + +# [END spanner_get_database_ddl] + + +# [START spanner_query_information_schema_database_options] +def query_information_schema_database_options(instance_id, database_id): + """Queries the default leader of a database.""" + spanner_client = spanner.Client() + instance = spanner_client.instance(instance_id) + database = instance.database(database_id) + with database.snapshot() as snapshot: + results = snapshot.execute_sql( + "SELECT OPTION_VALUE AS default_leader " + "FROM INFORMATION_SCHEMA.DATABASE_OPTIONS " + "WHERE SCHEMA_NAME = '' AND OPTION_NAME = 'default_leader'" + ) + for result in results: + print("Database {} has default leader {}".format( + database_id, result[0] + )) + + +# [END spanner_query_information_schema_database_options] + + # [START spanner_insert_data] def insert_data(instance_id, database_id): """Inserts sample data into the given database. diff --git a/samples/samples/snippets_test.py b/samples/samples/snippets_test.py index 4a8d1991d3..44f66db6d9 100644 --- a/samples/samples/snippets_test.py +++ b/samples/samples/snippets_test.py @@ -15,12 +15,12 @@ import time import uuid -from google.api_core import exceptions -from google.cloud import spanner import pytest +from google.api_core import exceptions from test_utils.retry import RetryErrors import snippets +from google.cloud import spanner CREATE_TABLE_SINGERS = """\ CREATE TABLE Singers ( @@ -58,6 +58,12 @@ def lci_instance_id(): return f"lci-instance-{uuid.uuid4().hex[:10]}" +@pytest.fixture(scope="module") +def default_leader_instance_id(): + """Id for the default leader instance.""" + return f"default-leader-instance-{uuid.uuid4().hex[:10]}" + + @pytest.fixture(scope="module") def database_id(): return f"test-db-{uuid.uuid4().hex[:10]}" @@ -73,6 +79,11 @@ def cmek_database_id(): return f"cmek-db-{uuid.uuid4().hex[:10]}" +@pytest.fixture(scope="module") +def default_leader_database_id(): + return f"default-leader-db-{uuid.uuid4().hex[:10]}" + + @pytest.fixture(scope="module") def database_ddl(): """Sequence of DDL statements used to set up the database. @@ -119,6 +130,71 @@ def test_create_database_with_encryption_config(capsys, instance_id, cmek_databa assert kms_key_name in out +def test_get_instance_config(capsys): + instance_config = "nam6" + snippets.get_instance_config(instance_config) + out, _ = capsys.readouterr() + assert instance_config in out + + +def test_list_instance_config(capsys): + snippets.list_instance_config() + out, _ = capsys.readouterr() + assert "Available instance configs" in out + + +def test_list_databases(capsys, instance_id): + snippets.list_databases(instance_id) + out, _ = capsys.readouterr() + assert "Databases" in out + assert "Default leaders of databases" in out + + +def test_create_database_with_default_leader(capsys, default_leader_instance_id, default_leader_database_id): + multi_region_confg = "nam3" + default_leader = "us-east4" + retry_429 = RetryErrors(exceptions.ResourceExhausted, delay=15) + retry_429(snippets.create_database_with_default_leader)( + default_leader_instance_id, default_leader_database_id, + multi_region_confg, default_leader + ) + out, _ = capsys.readouterr() + assert default_leader_database_id in out + assert default_leader in out + spanner_client = spanner.Client() + instance = spanner_client.instance(default_leader_instance_id) + instance.delete() + + +def test_update_database_with_default_leader(capsys, default_leader_instance_id, + default_leader_database_id): + multi_region_confg = "nam3" + default_leader = "us-east4" + retry_429 = RetryErrors(exceptions.ResourceExhausted, delay=15) + retry_429(snippets.update_database_with_default_leader)( + default_leader_instance_id, default_leader_database_id, + multi_region_confg, default_leader + ) + out, _ = capsys.readouterr() + assert default_leader_database_id in out + assert default_leader in out + spanner_client = spanner.Client() + instance = spanner_client.instance(default_leader_instance_id) + instance.delete() + + +def test_get_database_ddl(capsys, instance_id, sample_database): + snippets.get_database_ddl(instance_id, sample_database.database_id) + out, _ = capsys.readouterr() + assert sample_database.database_id in out + + +def test_query_information_schema_database_options(capsys, instance_id, sample_database): + snippets.query_information_schema_database_options(instance_id, sample_database.database_id) + out, _ = capsys.readouterr() + assert "has default leader" in out + + @pytest.mark.dependency(name="insert_data") def test_insert_data(capsys, instance_id, sample_database): snippets.insert_data(instance_id, sample_database.database_id) From 3e9137c29cc5b1153c26b9b9167cc309a50e8c6b Mon Sep 17 00:00:00 2001 From: Zoe Cai Date: Tue, 27 Jul 2021 11:59:43 +1000 Subject: [PATCH 08/17] fix --- samples/samples/snippets.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/samples/snippets.py b/samples/samples/snippets.py index 3cf75c46eb..38cb44c5ed 100644 --- a/samples/samples/snippets.py +++ b/samples/samples/snippets.py @@ -99,9 +99,9 @@ def create_instance_with_processing_units(instance_id, processing_units): # [START spanner_get_instance_config] def get_instance_config(instance_config): """Gets the leader options for the instance configuration.""" - from google.cloud.spanner_admin_instance_v1 import GetInstanceConfigRequest spanner_client = spanner.Client() - config = spanner_client.instance_admin_api.get_instance_config(name=instance_config) + config_name = "{}/instanceConfigs/{}".format(spanner_client.project_name, instance_config) + config = spanner_client.instance_admin_api.get_instance_config(name=config_name) print("Available leader options for instance config {}: {}".format( instance_config, config.leader_options)) @@ -316,7 +316,7 @@ def get_database_ddl(instance_id, database_id): spanner_client = spanner.Client() instance = spanner_client.instance(instance_id) database = instance.database(database_id) - ddl = spanner_client.database_admin_api.get_database_ddl(database.name) + ddl = spanner_client.database_admin_api.get_database_ddl(database=database.name) print("Retrieved database DDL for {}".format(database_id)) for statement in ddl.statements: print(statement) From 384e1a2e9df31ce316b9905ff2bc7f2b1aae3d5f Mon Sep 17 00:00:00 2001 From: Zoe Cai Date: Tue, 27 Jul 2021 12:59:56 +1000 Subject: [PATCH 09/17] fix --- samples/samples/snippets.py | 4 ++-- samples/samples/snippets_test.py | 20 ++++++++++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/samples/samples/snippets.py b/samples/samples/snippets.py index 38cb44c5ed..fa45fbff55 100644 --- a/samples/samples/snippets.py +++ b/samples/samples/snippets.py @@ -210,12 +210,12 @@ def create_database_with_encryption_key(instance_id, database_id, kms_key_name): # [START spanner_create_database_with_default_leader] def create_database_with_default_leader( - instance_id, database_id, configRegion, default_leader + instance_id, database_id, config_region, default_leader ): """Creates a database with tables with a default leader.""" spanner_client = spanner.Client() - config_name = "{}/instanceConfigs/{}".format(spanner_client.project_name, configRegion) + config_name = "{}/instanceConfigs/{}".format(spanner_client.project_name, config_region) instance = spanner_client.instance( instance_id, configuration_name=config_name ) diff --git a/samples/samples/snippets_test.py b/samples/samples/snippets_test.py index 44f66db6d9..8df1ed79b6 100644 --- a/samples/samples/snippets_test.py +++ b/samples/samples/snippets_test.py @@ -61,7 +61,7 @@ def lci_instance_id(): @pytest.fixture(scope="module") def default_leader_instance_id(): """Id for the default leader instance.""" - return f"default-leader-instance-{uuid.uuid4().hex[:10]}" + return f"default-leader-instance-{uuid.uuid4().hex[:8]}" @pytest.fixture(scope="module") @@ -189,10 +189,22 @@ def test_get_database_ddl(capsys, instance_id, sample_database): assert sample_database.database_id in out -def test_query_information_schema_database_options(capsys, instance_id, sample_database): - snippets.query_information_schema_database_options(instance_id, sample_database.database_id) +def test_query_information_schema_database_options(capsys, default_leader_instance_id, default_leader_database_id): + multi_region_confg = "nam3" + default_leader = "us-east4" + retry_429 = RetryErrors(exceptions.ResourceExhausted, delay=15) + retry_429(snippets.create_database_with_default_leader)( + default_leader_instance_id, default_leader_database_id, + multi_region_confg, default_leader + ) + snippets.query_information_schema_database_options( + default_leader_instance_id, default_leader_database_id + ) out, _ = capsys.readouterr() - assert "has default leader" in out + assert default_leader in out + spanner_client = spanner.Client() + instance = spanner_client.instance(default_leader_instance_id) + instance.delete() @pytest.mark.dependency(name="insert_data") From 800430042a321adb026e871dd2dae8cbed46c06c Mon Sep 17 00:00:00 2001 From: Zoe Cai Date: Tue, 27 Jul 2021 13:56:16 +1000 Subject: [PATCH 10/17] fix --- samples/samples/snippets_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/samples/snippets_test.py b/samples/samples/snippets_test.py index 8df1ed79b6..bd7bef0f50 100644 --- a/samples/samples/snippets_test.py +++ b/samples/samples/snippets_test.py @@ -61,7 +61,7 @@ def lci_instance_id(): @pytest.fixture(scope="module") def default_leader_instance_id(): """Id for the default leader instance.""" - return f"default-leader-instance-{uuid.uuid4().hex[:8]}" + return f"default-leader-instance-{uuid.uuid4().hex[:6]}" @pytest.fixture(scope="module") From f88634b5d7e4c9a4a2038a05075ef7fed850874f Mon Sep 17 00:00:00 2001 From: Zoe Cai Date: Tue, 27 Jul 2021 18:51:33 +1000 Subject: [PATCH 11/17] use sample instance --- samples/samples/conftest.py | 2 +- samples/samples/snippets.py | 24 ++++----------------- samples/samples/snippets_test.py | 37 +++++++------------------------- 3 files changed, 13 insertions(+), 50 deletions(-) diff --git a/samples/samples/conftest.py b/samples/samples/conftest.py index 05c94f254f..bcb3496e2c 100644 --- a/samples/samples/conftest.py +++ b/samples/samples/conftest.py @@ -70,7 +70,7 @@ def instance_id(): @pytest.fixture(scope="module") def instance_config(spanner_client): return "{}/instanceConfigs/{}".format( - spanner_client.project_name, "regional-us-central1" + spanner_client.project_name, "nam3" ) diff --git a/samples/samples/snippets.py b/samples/samples/snippets.py index fa45fbff55..260a7104f6 100644 --- a/samples/samples/snippets.py +++ b/samples/samples/snippets.py @@ -210,19 +210,11 @@ def create_database_with_encryption_key(instance_id, database_id, kms_key_name): # [START spanner_create_database_with_default_leader] def create_database_with_default_leader( - instance_id, database_id, config_region, default_leader + instance_id, database_id, default_leader ): """Creates a database with tables with a default leader.""" spanner_client = spanner.Client() - - config_name = "{}/instanceConfigs/{}".format(spanner_client.project_name, config_region) - instance = spanner_client.instance( - instance_id, configuration_name=config_name - ) - operation = instance.create() - - print("Waiting for operation to complete...") - operation.result(120) + instance = spanner_client.instance(instance_id) database = instance.database( database_id, @@ -260,19 +252,11 @@ def create_database_with_default_leader( # [START spanner_update_database_with_default_leader] def update_database_with_default_leader( - instance_id, database_id, configRegion, default_leader + instance_id, database_id, default_leader ): """Updates a database with tables with a default leader.""" spanner_client = spanner.Client() - - config_name = "{}/instanceConfigs/{}".format(spanner_client.project_name, configRegion) - instance = spanner_client.instance( - instance_id, configuration_name=config_name - ) - operation = instance.create() - - print("Waiting for operation to complete...") - operation.result(120) + instance = spanner_client.instance(instance_id) database = instance.database( database_id, diff --git a/samples/samples/snippets_test.py b/samples/samples/snippets_test.py index bd7bef0f50..076810dddb 100644 --- a/samples/samples/snippets_test.py +++ b/samples/samples/snippets_test.py @@ -58,12 +58,6 @@ def lci_instance_id(): return f"lci-instance-{uuid.uuid4().hex[:10]}" -@pytest.fixture(scope="module") -def default_leader_instance_id(): - """Id for the default leader instance.""" - return f"default-leader-instance-{uuid.uuid4().hex[:6]}" - - @pytest.fixture(scope="module") def database_id(): return f"test-db-{uuid.uuid4().hex[:10]}" @@ -81,7 +75,7 @@ def cmek_database_id(): @pytest.fixture(scope="module") def default_leader_database_id(): - return f"default-leader-db-{uuid.uuid4().hex[:10]}" + return f"leader-db-{uuid.uuid4().hex[:10]}" @pytest.fixture(scope="module") @@ -150,37 +144,27 @@ def test_list_databases(capsys, instance_id): assert "Default leaders of databases" in out -def test_create_database_with_default_leader(capsys, default_leader_instance_id, default_leader_database_id): - multi_region_confg = "nam3" +def test_create_database_with_default_leader(capsys, instance_id, default_leader_database_id): default_leader = "us-east4" retry_429 = RetryErrors(exceptions.ResourceExhausted, delay=15) retry_429(snippets.create_database_with_default_leader)( - default_leader_instance_id, default_leader_database_id, - multi_region_confg, default_leader + instance_id, default_leader_database_id, default_leader ) out, _ = capsys.readouterr() assert default_leader_database_id in out assert default_leader in out - spanner_client = spanner.Client() - instance = spanner_client.instance(default_leader_instance_id) - instance.delete() -def test_update_database_with_default_leader(capsys, default_leader_instance_id, +def test_update_database_with_default_leader(capsys, instance_id, default_leader_database_id): - multi_region_confg = "nam3" default_leader = "us-east4" retry_429 = RetryErrors(exceptions.ResourceExhausted, delay=15) retry_429(snippets.update_database_with_default_leader)( - default_leader_instance_id, default_leader_database_id, - multi_region_confg, default_leader + instance_id, default_leader_database_id, default_leader ) out, _ = capsys.readouterr() assert default_leader_database_id in out assert default_leader in out - spanner_client = spanner.Client() - instance = spanner_client.instance(default_leader_instance_id) - instance.delete() def test_get_database_ddl(capsys, instance_id, sample_database): @@ -189,22 +173,17 @@ def test_get_database_ddl(capsys, instance_id, sample_database): assert sample_database.database_id in out -def test_query_information_schema_database_options(capsys, default_leader_instance_id, default_leader_database_id): - multi_region_confg = "nam3" +def test_query_information_schema_database_options(capsys, instance_id, default_leader_database_id): default_leader = "us-east4" retry_429 = RetryErrors(exceptions.ResourceExhausted, delay=15) retry_429(snippets.create_database_with_default_leader)( - default_leader_instance_id, default_leader_database_id, - multi_region_confg, default_leader + instance_id, default_leader_database_id, default_leader ) snippets.query_information_schema_database_options( - default_leader_instance_id, default_leader_database_id + instance_id, default_leader_database_id ) out, _ = capsys.readouterr() assert default_leader in out - spanner_client = spanner.Client() - instance = spanner_client.instance(default_leader_instance_id) - instance.delete() @pytest.mark.dependency(name="insert_data") From 8d95511803da561274e39950b0352b59c26658eb Mon Sep 17 00:00:00 2001 From: Zoe Cai Date: Tue, 27 Jul 2021 20:01:06 +1000 Subject: [PATCH 12/17] use new default leader instance --- samples/samples/conftest.py | 49 ++++++++++++++++++++++++++++++++ samples/samples/snippets_test.py | 15 +++++----- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/samples/samples/conftest.py b/samples/samples/conftest.py index bcb3496e2c..ef3e0b1079 100644 --- a/samples/samples/conftest.py +++ b/samples/samples/conftest.py @@ -67,8 +67,21 @@ def instance_id(): return f"test-instance-{uuid.uuid4().hex[:10]}" +@pytest.fixture(scope="module") +def leader_instance_id(): + """Unique id for the instance used in default leader samples.""" + return f"leader-instance-{uuid.uuid4().hex[:10]}" + + @pytest.fixture(scope="module") def instance_config(spanner_client): + return "{}/instanceConfigs/{}".format( + spanner_client.project_name, "regional-us-central1" + ) + + +@pytest.fixture(scope="module") +def multi_region_instance_config(spanner_client): return "{}/instanceConfigs/{}".format( spanner_client.project_name, "nam3" ) @@ -110,6 +123,42 @@ def sample_instance( sample_instance.delete() +@pytest.fixture(scope="module") +def multi_region_instance( + spanner_client, + cleanup_old_instances, + leader_instance_id, + multi_region_instance_config, + sample_name, +): + multi_region_instance = spanner_client.instance( + leader_instance_id, + multi_region_instance_config, + labels={ + "cloud_spanner_samples": "true", + "sample_name": sample_name, + "created": str(int(time.time())) + }, + ) + retry_429 = retry.RetryErrors(exceptions.ResourceExhausted, delay=15) + op = retry_429(multi_region_instance.create)() + op.result(120) # block until completion + + # Eventual consistency check + retry_found = retry.RetryResult(bool) + retry_found(multi_region_instance.exists)() + + yield multi_region_instance + + for database_pb in multi_region_instance.list_databases(): + database.Database.from_pb(database_pb, multi_region_instance).drop() + + for backup_pb in multi_region_instance.list_backups(): + backup.Backup.from_pb(backup_pb, multi_region_instance).delete() + + multi_region_instance.delete() + + @pytest.fixture(scope="module") def database_id(): """Id for the database used in samples. diff --git a/samples/samples/snippets_test.py b/samples/samples/snippets_test.py index 076810dddb..1d7167709e 100644 --- a/samples/samples/snippets_test.py +++ b/samples/samples/snippets_test.py @@ -144,23 +144,22 @@ def test_list_databases(capsys, instance_id): assert "Default leaders of databases" in out -def test_create_database_with_default_leader(capsys, instance_id, default_leader_database_id): +def test_create_database_with_default_leader(capsys, multi_region_instance, default_leader_database_id): default_leader = "us-east4" retry_429 = RetryErrors(exceptions.ResourceExhausted, delay=15) retry_429(snippets.create_database_with_default_leader)( - instance_id, default_leader_database_id, default_leader + multi_region_instance, default_leader_database_id, default_leader ) out, _ = capsys.readouterr() assert default_leader_database_id in out assert default_leader in out -def test_update_database_with_default_leader(capsys, instance_id, - default_leader_database_id): +def test_update_database_with_default_leader(capsys, multi_region_instance, default_leader_database_id): default_leader = "us-east4" retry_429 = RetryErrors(exceptions.ResourceExhausted, delay=15) retry_429(snippets.update_database_with_default_leader)( - instance_id, default_leader_database_id, default_leader + multi_region_instance, default_leader_database_id, default_leader ) out, _ = capsys.readouterr() assert default_leader_database_id in out @@ -173,14 +172,14 @@ def test_get_database_ddl(capsys, instance_id, sample_database): assert sample_database.database_id in out -def test_query_information_schema_database_options(capsys, instance_id, default_leader_database_id): +def test_query_information_schema_database_options(capsys, multi_region_instance, default_leader_database_id): default_leader = "us-east4" retry_429 = RetryErrors(exceptions.ResourceExhausted, delay=15) retry_429(snippets.create_database_with_default_leader)( - instance_id, default_leader_database_id, default_leader + multi_region_instance, default_leader_database_id, default_leader ) snippets.query_information_schema_database_options( - instance_id, default_leader_database_id + multi_region_instance, default_leader_database_id ) out, _ = capsys.readouterr() assert default_leader in out From 635b32d3232b9e301fc1c4699415582644e228aa Mon Sep 17 00:00:00 2001 From: Zoe Cai Date: Tue, 27 Jul 2021 20:57:45 +1000 Subject: [PATCH 13/17] fix --- samples/samples/conftest.py | 10 +++++----- samples/samples/snippets.py | 24 ++---------------------- samples/samples/snippets_test.py | 25 ++++++++++++------------- 3 files changed, 19 insertions(+), 40 deletions(-) diff --git a/samples/samples/conftest.py b/samples/samples/conftest.py index ef3e0b1079..5aa74bf943 100644 --- a/samples/samples/conftest.py +++ b/samples/samples/conftest.py @@ -68,9 +68,9 @@ def instance_id(): @pytest.fixture(scope="module") -def leader_instance_id(): - """Unique id for the instance used in default leader samples.""" - return f"leader-instance-{uuid.uuid4().hex[:10]}" +def multi_region_instance_id(): + """Unique id for the multi-region instance used in samples.""" + return f"multi-instance-{uuid.uuid4().hex[:10]}" @pytest.fixture(scope="module") @@ -127,12 +127,12 @@ def sample_instance( def multi_region_instance( spanner_client, cleanup_old_instances, - leader_instance_id, + multi_region_instance_id, multi_region_instance_config, sample_name, ): multi_region_instance = spanner_client.instance( - leader_instance_id, + multi_region_instance_id, multi_region_instance_config, labels={ "cloud_spanner_samples": "true", diff --git a/samples/samples/snippets.py b/samples/samples/snippets.py index 260a7104f6..0913e1678a 100644 --- a/samples/samples/snippets.py +++ b/samples/samples/snippets.py @@ -258,29 +258,9 @@ def update_database_with_default_leader( spanner_client = spanner.Client() instance = spanner_client.instance(instance_id) - database = instance.database( - database_id, - 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""", - ], - ) - operation = database.create() - - print("Waiting for operation to complete...") - operation.result(120) + database = instance.database(database_id) - database.update_ddl(["ALTER DATABASE {}" + operation = database.update_ddl(["ALTER DATABASE {}" " SET OPTIONS (default_leader = '{}')".format(database_id, default_leader)]) operation.result(120) diff --git a/samples/samples/snippets_test.py b/samples/samples/snippets_test.py index 1d7167709e..d32cb4cc3f 100644 --- a/samples/samples/snippets_test.py +++ b/samples/samples/snippets_test.py @@ -87,6 +87,12 @@ def database_ddl(): return [CREATE_TABLE_SINGERS, CREATE_TABLE_ALBUMS] +@pytest.fixture(scope="module") +def default_leader(): + """ Default leader for multi-region instances. """ + return "us-east4" + + def test_create_instance_explicit(spanner_client, create_instance_id): # Rather than re-use 'sample_isntance', we create a new instance, to # ensure that the 'create_instance' snippet is tested. @@ -144,22 +150,20 @@ def test_list_databases(capsys, instance_id): assert "Default leaders of databases" in out -def test_create_database_with_default_leader(capsys, multi_region_instance, default_leader_database_id): - default_leader = "us-east4" +def test_create_database_with_default_leader(capsys, multi_region_instance_id, default_leader_database_id, default_leader): retry_429 = RetryErrors(exceptions.ResourceExhausted, delay=15) retry_429(snippets.create_database_with_default_leader)( - multi_region_instance, default_leader_database_id, default_leader + multi_region_instance_id, default_leader_database_id, default_leader ) out, _ = capsys.readouterr() assert default_leader_database_id in out assert default_leader in out -def test_update_database_with_default_leader(capsys, multi_region_instance, default_leader_database_id): - default_leader = "us-east4" +def test_update_database_with_default_leader(capsys, multi_region_instance_id, default_leader_database_id, default_leader): retry_429 = RetryErrors(exceptions.ResourceExhausted, delay=15) retry_429(snippets.update_database_with_default_leader)( - multi_region_instance, default_leader_database_id, default_leader + multi_region_instance_id, default_leader_database_id, default_leader ) out, _ = capsys.readouterr() assert default_leader_database_id in out @@ -172,14 +176,9 @@ def test_get_database_ddl(capsys, instance_id, sample_database): assert sample_database.database_id in out -def test_query_information_schema_database_options(capsys, multi_region_instance, default_leader_database_id): - default_leader = "us-east4" - retry_429 = RetryErrors(exceptions.ResourceExhausted, delay=15) - retry_429(snippets.create_database_with_default_leader)( - multi_region_instance, default_leader_database_id, default_leader - ) +def test_query_information_schema_database_options(capsys, multi_region_instance_id, default_leader_database_id, default_leader): snippets.query_information_schema_database_options( - multi_region_instance, default_leader_database_id + multi_region_instance_id, default_leader_database_id ) out, _ = capsys.readouterr() assert default_leader in out From f4f2ede9968211e85a938661cbbe306fc4e8165a Mon Sep 17 00:00:00 2001 From: Zoe Cai Date: Tue, 27 Jul 2021 21:46:19 +1000 Subject: [PATCH 14/17] underscore rather than dashes? --- samples/samples/snippets_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/samples/snippets_test.py b/samples/samples/snippets_test.py index d32cb4cc3f..f162d1364f 100644 --- a/samples/samples/snippets_test.py +++ b/samples/samples/snippets_test.py @@ -15,12 +15,12 @@ import time import uuid -import pytest from google.api_core import exceptions +from google.cloud import spanner from test_utils.retry import RetryErrors +import pytest import snippets -from google.cloud import spanner CREATE_TABLE_SINGERS = """\ CREATE TABLE Singers ( @@ -75,7 +75,7 @@ def cmek_database_id(): @pytest.fixture(scope="module") def default_leader_database_id(): - return f"leader-db-{uuid.uuid4().hex[:10]}" + return f"leader_db_{uuid.uuid4().hex[:10]}" @pytest.fixture(scope="module") From c68393e60ebd4aa35b785b171d1039c25fb15342 Mon Sep 17 00:00:00 2001 From: Zoe Cai Date: Wed, 28 Jul 2021 12:03:42 +1000 Subject: [PATCH 15/17] fix --- samples/samples/snippets.py | 4 ++-- samples/samples/snippets_test.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/samples/samples/snippets.py b/samples/samples/snippets.py index 0913e1678a..ba4fa7d358 100644 --- a/samples/samples/snippets.py +++ b/samples/samples/snippets.py @@ -242,7 +242,7 @@ def create_database_with_default_leader( print( "Database {} created with default leader {}".format( - database.name, database.default_leader + database.name, database.default_leader ) ) @@ -261,7 +261,7 @@ def update_database_with_default_leader( database = instance.database(database_id) operation = database.update_ddl(["ALTER DATABASE {}" - " SET OPTIONS (default_leader = '{}')".format(database_id, default_leader)]) + " SET OPTIONS (default_leader = '{}')".format(database_id, default_leader)]) operation.result(120) print( diff --git a/samples/samples/snippets_test.py b/samples/samples/snippets_test.py index f162d1364f..de794f24fb 100644 --- a/samples/samples/snippets_test.py +++ b/samples/samples/snippets_test.py @@ -17,9 +17,9 @@ from google.api_core import exceptions from google.cloud import spanner +import pytest from test_utils.retry import RetryErrors -import pytest import snippets CREATE_TABLE_SINGERS = """\ @@ -150,7 +150,7 @@ def test_list_databases(capsys, instance_id): assert "Default leaders of databases" in out -def test_create_database_with_default_leader(capsys, multi_region_instance_id, default_leader_database_id, default_leader): +def test_create_database_with_default_leader(capsys, multi_region_instance, multi_region_instance_id, default_leader_database_id, default_leader): retry_429 = RetryErrors(exceptions.ResourceExhausted, delay=15) retry_429(snippets.create_database_with_default_leader)( multi_region_instance_id, default_leader_database_id, default_leader @@ -160,7 +160,7 @@ def test_create_database_with_default_leader(capsys, multi_region_instance_id, d assert default_leader in out -def test_update_database_with_default_leader(capsys, multi_region_instance_id, default_leader_database_id, default_leader): +def test_update_database_with_default_leader(capsys, multi_region_instance, multi_region_instance_id, default_leader_database_id, default_leader): retry_429 = RetryErrors(exceptions.ResourceExhausted, delay=15) retry_429(snippets.update_database_with_default_leader)( multi_region_instance_id, default_leader_database_id, default_leader @@ -176,7 +176,7 @@ def test_get_database_ddl(capsys, instance_id, sample_database): assert sample_database.database_id in out -def test_query_information_schema_database_options(capsys, multi_region_instance_id, default_leader_database_id, default_leader): +def test_query_information_schema_database_options(capsys, multi_region_instance, multi_region_instance_id, default_leader_database_id, default_leader): snippets.query_information_schema_database_options( multi_region_instance_id, default_leader_database_id ) From 86a64a2c7039a16795d715ce5023603abb74a04c Mon Sep 17 00:00:00 2001 From: Zoe Cai Date: Wed, 28 Jul 2021 13:26:47 +1000 Subject: [PATCH 16/17] add reload --- samples/samples/snippets.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/samples/samples/snippets.py b/samples/samples/snippets.py index ba4fa7d358..c3ed80c484 100644 --- a/samples/samples/snippets.py +++ b/samples/samples/snippets.py @@ -240,6 +240,8 @@ def create_database_with_default_leader( print("Waiting for operation to complete...") operation.result(120) + database.reload() + print( "Database {} created with default leader {}".format( database.name, database.default_leader @@ -264,6 +266,8 @@ def update_database_with_default_leader( " SET OPTIONS (default_leader = '{}')".format(database_id, default_leader)]) operation.result(120) + database.reload() + print( "Database {} updated with default leader {}".format( database.name, database.default_leader From f46ee7475b1b08c2a3a42f5127c3a9cc59a352e5 Mon Sep 17 00:00:00 2001 From: Zoe Cai Date: Tue, 3 Aug 2021 13:27:02 +1000 Subject: [PATCH 17/17] review changes --- samples/samples/snippets.py | 17 ++++++++++++----- samples/samples/snippets_test.py | 5 ++--- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/samples/samples/snippets.py b/samples/samples/snippets.py index c3ed80c484..0cc68856ea 100644 --- a/samples/samples/snippets.py +++ b/samples/samples/snippets.py @@ -114,7 +114,12 @@ def list_instance_config(): """Lists the available instance configurations.""" spanner_client = spanner.Client() configs = spanner_client.list_instance_configs() - print("Available instance configs: {}".format(configs)) + for config in configs: + print( + "Available leader options for instance config {}: {}".format( + config.name, config.leader_options + ) + ) # [END spanner_list_instance_configs] @@ -127,10 +132,12 @@ def list_databases(instance_id): instance = spanner_client.instance(instance_id) databases = list(instance.list_databases()) - default_leaders = [database.default_leader for database in databases] - - print("Databases: {}".format(databases)) - print("Default leaders of databases: {}".format(default_leaders)) + for database in databases: + print( + "Database {} has default leader {}".format( + database.name, database.default_leader + ) + ) # [END spanner_list_databases] diff --git a/samples/samples/snippets_test.py b/samples/samples/snippets_test.py index de794f24fb..636b4b5e91 100644 --- a/samples/samples/snippets_test.py +++ b/samples/samples/snippets_test.py @@ -140,14 +140,13 @@ def test_get_instance_config(capsys): def test_list_instance_config(capsys): snippets.list_instance_config() out, _ = capsys.readouterr() - assert "Available instance configs" in out + assert "regional-us-central1" in out def test_list_databases(capsys, instance_id): snippets.list_databases(instance_id) out, _ = capsys.readouterr() - assert "Databases" in out - assert "Default leaders of databases" in out + assert "has default leader" in out def test_create_database_with_default_leader(capsys, multi_region_instance, multi_region_instance_id, default_leader_database_id, default_leader):