Database Admin Usage¶
After creating an Instance
, you can
interact with individual databases for that instance.
List Databases¶
To iterate over all existing databases for an instance, use its
list_databases()
method:
for database in instance.list_databases():
# `database` is a `Database` object.
This method yields Database
objects.
Database Factory¶
To create a Database
object:
database = instance.database(database_id, ddl_statements)
ddl_statements
is a list of strings containing DDL statements for the new database.
You can also use the database()
method
on an Instance
object to create a local wrapper
for a database that has already been created:
database = instance.database(existing_database_id)
Create a new Database¶
After creating the database object, use its
create()
method to
trigger its creation on the server:
operation = database.create()
Update an existing Database¶
After creating the database object, you can apply additional DDL statements
via its update_ddl()
method:
operation = database.update_ddl(ddl_statements, operation_id)
ddl_statements
is a list of strings containing DDL statements to be applied to the database.operation_id
is a string ID for the long-running operation.
Note
Updating a database triggers a “long-running operation” and
returns an Operation
object. See Check on Current Database Operation for polling
to find out if the operation is completed.
Drop a Database¶
Drop a database using its
drop()
method:
database.drop()
Check on Current Database Operation¶
The create()
and
update_ddl()
methods of the
Database
object trigger
long-running operations on the server, and return operations
conforming to the Future
class.
>>> operation = database.create()
>>> operation.result()
Non-Admin Database Usage¶
Use a Snapshot to Read / Query the Database¶
A snapshot represents a read-only point-in-time view of the database.
Calling snapshot()
with
no arguments creates a snapshot with strong concurrency:
with database.snapshot() as snapshot:
do_something_with(snapshot)
See Snapshot
for the other options
which can be passed.
Note
snapshot()
returns an
object intended to be used as a Python context manager (i.e., as the
target of a with
statement). Perform all iterations within the
context of the with database.snapshot()
block.
See Read-only Transactions via Snapshots for more complete examples of snapshot usage.
Use a Batch to Modify Rows in the Database¶
A batch represents a bundled set of insert/upsert/update/delete operations on the rows of tables in the database.
with database.batch() as batch:
batch.insert_or_update(table, columns, rows)
batch.delete(table, keyset_to_delete)
Note
batch()
returns an
object intended to be used as a Python context manager (i.e., as the
target of a with
statement). It applies any changes made inside
the block of its with
statement when exiting the block, unless an
exception is raised within the block. Use the batch only inside
the block created by the with
statement.
See Batching Modifications for more complete examples of batch usage.
Use a Transaction to Query / Modify Rows in the Database¶
A transaction represents the union of a “strong” snapshot and a batch:
it allows read
and execute_sql
operations, and accumulates
insert/upsert/update/delete operations.
Because other applications may be performing concurrent updates which would invalidate the reads / queries, the work done by a transaction needs to be bundled as a retryable “unit of work” function, which takes the transaction as a required argument:
def unit_of_work(transaction):
result = transaction.execute_sql(QUERY)
for emp_id, hours, pay in _compute_pay(result):
transaction.insert_or_update(
table='monthly_hours',
columns=['employee_id', 'month', 'hours', 'pay'],
values=[emp_id, month_start, hours, pay])
database.run_in_transaction(unit_of_work)
Note
run_in_transaction()
commits the transaction automatically if the “unit of work” function
returns without raising an exception.
Note
run_in_transaction()
retries the “unit of work” function if the read / query operations
or the commit are aborted due to concurrent updates.
See Read-write Transactions for more complete examples of transaction usage.
Configuring a session pool for a database¶
Under the covers, the snapshot
, batch
, and run_in_transaction
methods use a pool of Session
objects
to manage their communication with the back-end. You can configure
one of the pools manually to control the number of sessions, timeouts, etc.,
and then pass it to the Database
constructor:
from google.cloud import spanner
# Instantiate the Spanner client, and get the appropriate instance.
client = spanner.Client()
instance = client.instance(INSTANCE_NAME)
# Create a database with a pool of a fixed size.
pool = spanner.FixedSizePool(size=10, default_timeout=5)
database = instance.database(DATABASE_NAME, pool=pool)
Note that creating a database with a pool will require the database to already exist if the pool implementation needs to pre-create sessions (rather than creating them on demand, as the default implementation does).
You can supply your own pool implementation, which must satisfy the
contract laid out in AbstractSessionPool
:
from google.cloud.spanner import AbstractSessionPool
class MyCustomPool(AbstractSessionPool):
def __init__(self, database, custom_param):
super(MyCustomPool, self).__init__(database)
self.custom_param = custom_param
def get(self, read_only=False):
...
def put(self, session, discard_if_full=True):
...
database = instance.database(DATABASE_NAME, pool=pool)
pool = MyCustomPool(database, custom_param=42)
See Advanced Session Pool Topics for more advanced coverage of session pools.