-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat: add send and ack mutations Cloud Spanner Queues #17728
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| from google.cloud.spanner_admin_instance_v1.types import spanner_instance_admin | ||
| request = spanner_instance_admin.UpdateInstanceRequest( | ||
| instance=spanner_instance_admin.Instance( | ||
| name="projects/my-project/instances/my-instance", | ||
| edition=spanner_instance_admin.Instance.Edition.ENTERPRISE, | ||
| ), | ||
| field_mask={"paths": ["edition"]}, | ||
| ) | ||
| print("SUCCESS") | ||
|
Comment on lines
+1
to
+9
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -138,12 +138,36 @@ async def shared_instance( | |||||
| instance_config, | ||||||
| ): | ||||||
| spanner_client._instance_admin_api = None | ||||||
| instance = spanner_client.instance(shared_instance_id, instance_config.name) | ||||||
|
|
||||||
| if _helpers.CREATE_INSTANCE: | ||||||
| op = await instance.create() | ||||||
| await op.result(instance_operation_timeout) | ||||||
| import time | ||||||
|
|
||||||
| from google.cloud.spanner_admin_instance_v1.types import spanner_instance_admin | ||||||
|
|
||||||
| create_time = str(int(time.time())) | ||||||
| labels = {"python-spanner-systests": "true", "created": create_time} | ||||||
|
|
||||||
| request = spanner_instance_admin.CreateInstanceRequest( | ||||||
| parent=spanner_client.project_name, | ||||||
| instance_id=shared_instance_id, | ||||||
| instance=spanner_instance_admin.Instance( | ||||||
| config=instance_config.name, | ||||||
| display_name=shared_instance_id, | ||||||
| node_count=1, | ||||||
| labels=labels, | ||||||
| edition=spanner_instance_admin.Instance.Edition.ENTERPRISE_PLUS, | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using the
Suggested change
|
||||||
| ), | ||||||
| ) | ||||||
| created_op = await spanner_client.instance_admin_api.create_instance( | ||||||
| request=request | ||||||
| ) | ||||||
| await created_op.result(instance_operation_timeout) | ||||||
|
|
||||||
| instance = spanner_client.instance( | ||||||
| shared_instance_id, instance_config.name, labels=labels | ||||||
| ) | ||||||
| else: | ||||||
| instance = spanner_client.instance(shared_instance_id, instance_config.name) | ||||||
| await instance.reload() | ||||||
|
|
||||||
| yield instance | ||||||
|
|
@@ -205,3 +229,9 @@ async def databases_to_delete(): | |||||
| def not_postgres(database_dialect): | ||||||
| if database_dialect == DatabaseDialect.POSTGRESQL: | ||||||
| pytest.skip("Skip for Postgres") | ||||||
|
|
||||||
|
|
||||||
| @pytest.fixture(scope="function") | ||||||
| def not_emulator(): | ||||||
| if _helpers.USE_EMULATOR: | ||||||
| pytest.skip(f"{_helpers.USE_EMULATOR_ENVVAR} set in environment.") | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -87,6 +87,76 @@ async def test_db_batch_insert_then_db_snapshot_read(shared_database): | |
| sd._check_rows_data(from_snap) | ||
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_db_batch_send_and_ack( | ||
| not_emulator, spanner_client, database_dialect, shared_instance | ||
| ): | ||
| import uuid | ||
|
|
||
| from google.api_core.exceptions import GoogleAPIError, MethodNotImplemented | ||
|
|
||
| from google.cloud.spanner_admin_database_v1 import DatabaseDialect | ||
|
|
||
| db_name = f"test-db-{uuid.uuid4().hex[:8]}" | ||
| queue_name = f"test_queue_{uuid.uuid4().hex[:8]}" | ||
|
|
||
| test_database = await shared_instance.database( | ||
| db_name, database_dialect=database_dialect | ||
| ) | ||
| operation = await test_database.create() | ||
| operation.result(300) | ||
|
|
||
| try: | ||
| # Create the Queue | ||
| if database_dialect == DatabaseDialect.POSTGRESQL: | ||
| queue_ddl = f"""CREATE QUEUE {queue_name} ( | ||
| id bigint NOT NULL, | ||
| "Payload" varchar NOT NULL, | ||
| PRIMARY KEY (id) | ||
| )""" | ||
| else: | ||
| queue_ddl = f"""CREATE QUEUE {queue_name} ( | ||
| Id INT64 NOT NULL, | ||
| Payload STRING(MAX) NOT NULL | ||
| ) PRIMARY KEY (Id)""" | ||
|
|
||
| try: | ||
| operation = await test_database.update_ddl([queue_ddl]) | ||
| await operation.result(600) | ||
| except MethodNotImplemented as e: | ||
| pytest.skip(f"Queues are not implemented yet: {e}") | ||
| except GoogleAPIError as e: | ||
| if ( | ||
| getattr(e, "code", None) == 501 | ||
| or ( | ||
| getattr(e, "grpc_status_code", None) | ||
| and e.grpc_status_code.name == "UNIMPLEMENTED" | ||
| ) | ||
| or "UNIMPLEMENTED" in str(e) | ||
| ): | ||
| pytest.skip(f"Queues are not implemented yet: {e}") | ||
| raise | ||
|
Comment on lines
+128
to
+138
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Checking except GoogleAPIError as e:
grpc_status_name = getattr(getattr(e, "grpc_status_code", None), "name", None)
if (
getattr(e, "code", None) == 501
or grpc_status_name == "UNIMPLEMENTED"
or "UNIMPLEMENTED" in str(e)
):
pytest.skip(f"Queues are not implemented yet: {e}")
raise |
||
|
|
||
| # Run mutations | ||
| async with test_database.batch() as batch: | ||
| batch.send( | ||
| queue=queue_name, | ||
| key=(2,), | ||
| payload="Hello, Queues!", | ||
| ) | ||
|
|
||
| print("Acking message in queue...") | ||
| async with test_database.batch() as batch: | ||
| batch.ack( | ||
| queue=queue_name, | ||
| key=(2,), | ||
| ) | ||
|
|
||
| finally: | ||
| print("Dropping database...") | ||
| await test_database.drop() | ||
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_db_run_in_transaction_then_snapshot_execute_sql(shared_database): | ||
| await shared_database.reload() | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -205,14 +205,30 @@ def shared_instance( | |||||
| _helpers.cleanup_old_instances(spanner_client) | ||||||
|
|
||||||
| if _helpers.CREATE_INSTANCE: | ||||||
| from google.cloud.spanner_admin_instance_v1.types import spanner_instance_admin | ||||||
|
|
||||||
| create_time = str(int(time.time())) | ||||||
| labels = {"python-spanner-systests": "true", "created": create_time} | ||||||
|
|
||||||
| request = spanner_instance_admin.CreateInstanceRequest( | ||||||
| parent=spanner_client.project_name, | ||||||
| instance_id=shared_instance_id, | ||||||
| instance=spanner_instance_admin.Instance( | ||||||
| config=instance_config.name, | ||||||
| display_name=shared_instance_id, | ||||||
| node_count=1, | ||||||
| labels=labels, | ||||||
| edition=spanner_instance_admin.Instance.Edition.ENTERPRISE_PLUS, | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using the
Suggested change
|
||||||
| ), | ||||||
| ) | ||||||
| created_op = _helpers.retry_429_503( | ||||||
| spanner_client.instance_admin_api.create_instance | ||||||
| )(request=request) | ||||||
| created_op.result(instance_operation_timeout) # block until completion | ||||||
|
|
||||||
| instance = spanner_client.instance( | ||||||
| shared_instance_id, instance_config.name, labels=labels | ||||||
| ) | ||||||
| created_op = _helpers.retry_429_503(instance.create)() | ||||||
| created_op.result(instance_operation_timeout) # block until completion | ||||||
|
|
||||||
| else: # reuse existing instance | ||||||
| instance = spanner_client.instance(shared_instance_id) | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -567,6 +567,75 @@ def test_db_batch_insert_then_db_snapshot_read(shared_database): | |||||||||||||||||||||||||||||||||||||
| sd._check_rows_data(from_snap) | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| def test_db_batch_send_and_ack( | ||||||||||||||||||||||||||||||||||||||
| not_emulator, spanner_client, database_dialect, shared_instance | ||||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||||
| import uuid | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| from google.api_core.exceptions import GoogleAPIError, MethodNotImplemented | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| from google.cloud.spanner_admin_database_v1 import DatabaseDialect | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| db_id = f"test-db-{uuid.uuid4().hex[:8]}" | ||||||||||||||||||||||||||||||||||||||
| queue_name = f"test_queue_{uuid.uuid4().hex[:8]}" | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| test_database = shared_instance.database(db_id, database_dialect=database_dialect) | ||||||||||||||||||||||||||||||||||||||
| operation = test_database.create() | ||||||||||||||||||||||||||||||||||||||
| operation.result(300) | ||||||||||||||||||||||||||||||||||||||
| print("Database created successfully!") | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||
| # Create the Queue | ||||||||||||||||||||||||||||||||||||||
| if database_dialect == DatabaseDialect.POSTGRESQL: | ||||||||||||||||||||||||||||||||||||||
| queue_ddl = f"""CREATE QUEUE {queue_name} ( | ||||||||||||||||||||||||||||||||||||||
| id bigint NOT NULL, | ||||||||||||||||||||||||||||||||||||||
| "Payload" varchar NOT NULL, | ||||||||||||||||||||||||||||||||||||||
| PRIMARY KEY (id) | ||||||||||||||||||||||||||||||||||||||
| )""" | ||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||
| queue_ddl = f"""CREATE QUEUE {queue_name} ( | ||||||||||||||||||||||||||||||||||||||
| Id INT64 NOT NULL, | ||||||||||||||||||||||||||||||||||||||
| Payload STRING(MAX) NOT NULL | ||||||||||||||||||||||||||||||||||||||
| ) PRIMARY KEY (Id)""" | ||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||
| operation = test_database.update_ddl([queue_ddl]) | ||||||||||||||||||||||||||||||||||||||
| operation.result(600) | ||||||||||||||||||||||||||||||||||||||
| except MethodNotImplemented as e: | ||||||||||||||||||||||||||||||||||||||
| pytest.skip(f"Queues are not implemented yet: {e}") | ||||||||||||||||||||||||||||||||||||||
| except GoogleAPIError as e: | ||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||
| getattr(e, "code", None) == 501 | ||||||||||||||||||||||||||||||||||||||
| or getattr(e, "grpc_status_code", None) | ||||||||||||||||||||||||||||||||||||||
| and e.grpc_status_code.name == "UNIMPLEMENTED" | ||||||||||||||||||||||||||||||||||||||
| or "UNIMPLEMENTED" in str(e) | ||||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||||
| pytest.skip(f"Queues are not implemented yet: {e}") | ||||||||||||||||||||||||||||||||||||||
| raise | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+605
to
+613
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Checking
Suggested change
|
||||||||||||||||||||||||||||||||||||||
| print("Queue created successfully.") | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # Run mutations | ||||||||||||||||||||||||||||||||||||||
| print("Sending message to queue...") | ||||||||||||||||||||||||||||||||||||||
| with test_database.batch() as batch: | ||||||||||||||||||||||||||||||||||||||
| batch.send( | ||||||||||||||||||||||||||||||||||||||
| queue=queue_name, | ||||||||||||||||||||||||||||||||||||||
| key=(2,), | ||||||||||||||||||||||||||||||||||||||
| payload="Hello, Queues!", | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
| print("Send successful.") | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| print("Acking message in queue...") | ||||||||||||||||||||||||||||||||||||||
| with test_database.batch() as batch: | ||||||||||||||||||||||||||||||||||||||
| batch.ack( | ||||||||||||||||||||||||||||||||||||||
| queue=queue_name, | ||||||||||||||||||||||||||||||||||||||
| key=(2,), | ||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||
| print("Ack successful.") | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| finally: | ||||||||||||||||||||||||||||||||||||||
| print("Dropping database...") | ||||||||||||||||||||||||||||||||||||||
| test_database.drop() | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| def test_db_run_in_transaction_then_snapshot_execute_sql(shared_database): | ||||||||||||||||||||||||||||||||||||||
| _helpers.retry_has_all_dll(shared_database.reload)() | ||||||||||||||||||||||||||||||||||||||
| sd = _sample_data | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this added by mistake?