Skip to content

Commit 002d650

Browse files
authored
tests: cleanup stale databases in spanner system tests (#17668)
### Problem Spanner system tests can sometimes leave behind test databases if a run is interrupted or fails to clean up properly. Over time, these stale databases can accumulate, potentially hitting quota limits or cluttering the test environment. ### Solution Introduced a proactive cleanup mechanism to remove stale databases during test setup. * **Added `cleanup_stale_databases` helper**: A new function in `_helpers.py` that lists databases in a given instance and drops any that are older than a specified cutoff (defaulting to 10 minutes/600 seconds). * **Drop Protection Handling**: The helper explicitly disables `enable_drop_protection` if it's set, ensuring that even protected test databases can be cleaned up automatically. * **Fixture Integration**: Integrated this cleanup call into the `shared_instance` fixture in `conftest.py`, ensuring that every test session starts by cleaning up residue from previous runs. ### Impact * Reduces the risk of test failures due to database quota exhaustion. * Keeps the Spanner instance used for testing clean. * Resilient against partial failures by handling `NotFound` and other API errors during cleanup.
1 parent 00b9040 commit 002d650

2 files changed

Lines changed: 28 additions & 3 deletions

File tree

packages/google-cloud-spanner/tests/system/_helpers.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@
1717
import time
1818
import uuid
1919

20-
from google.api_core import exceptions
20+
from google.api_core import datetime_helpers, exceptions
21+
from google.cloud.spanner_v1 import instance as instance_mod
2122
from test_utils import retry
2223

23-
from google.cloud.spanner_v1 import instance as instance_mod
2424
from tests import _fixtures
2525

2626
CREATE_INSTANCE_ENVVAR = "GOOGLE_CLOUD_TESTS_CREATE_SPANNER_INSTANCE"
@@ -159,6 +159,30 @@ def cleanup_old_instances(spanner_client):
159159
scrub_instance_ignore_not_found(instance)
160160

161161

162+
def cleanup_stale_databases(instance, cutoff_seconds=600):
163+
"""Delete stale databases in the given instance older than cutoff_seconds."""
164+
cutoff_ms = (int(time.time()) - cutoff_seconds) * 1000
165+
166+
for database_pb in instance.list_databases():
167+
if database_pb.create_time is not None:
168+
create_time_ms = datetime_helpers.to_milliseconds(database_pb.create_time)
169+
170+
if create_time_ms < cutoff_ms:
171+
db = instance.database(database_pb.name.split("/")[-1])
172+
try:
173+
db.reload()
174+
if db.enable_drop_protection:
175+
db.enable_drop_protection = False
176+
operation = db.update(["enable_drop_protection"])
177+
operation.result(DATABASE_OPERATION_TIMEOUT_IN_SECONDS)
178+
db.drop()
179+
except exceptions.NotFound:
180+
pass
181+
except exceptions.GoogleAPIError:
182+
# Ignore other API errors during cleanup
183+
pass
184+
185+
162186
def unique_id(prefix, separator="-"):
163187
# Database name size: Spanner database names are limited to 30 characters.
164188
# See: https://docs.cloud.google.com/spanner/docs/reference/rpc/google.spanner.admin.database.v1#createdatabaserequest

packages/google-cloud-spanner/tests/system/conftest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import time
1818

1919
import pytest
20-
2120
from google.cloud import spanner_v1
2221
from google.cloud.spanner_admin_database_v1 import DatabaseDialect
2322
from google.cloud.spanner_admin_database_v1.types.backup import (
@@ -218,6 +217,8 @@ def shared_instance(
218217
instance = spanner_client.instance(shared_instance_id)
219218
instance.reload()
220219

220+
_helpers.cleanup_stale_databases(instance)
221+
221222
yield instance
222223

223224
if _helpers.CREATE_INSTANCE:

0 commit comments

Comments
 (0)