Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions docs/spanner/advanced-session-pool-topics.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
Advanced Session Pool Topics
============================

Custom Session Pool Implementations
-----------------------------------

You can supply your own pool implementation, which must satisfy the
contract laid out in
:class:`~google.cloud.spanner.pool.AbstractSessionPool`:

.. code-block:: python

from google.cloud.spanner.pool import AbstractSessionPool

class MyCustomPool(AbstractSessionPool):

def __init__(self, custom_param):
super(MyCustomPool, self).__init__()
self.custom_param = custom_param

def bind(self, database):
...

def get(self, read_only=False):
...

def put(self, session, discard_if_full=True):
...

pool = MyCustomPool(custom_param=42)
database = instance.database(DATABASE_NAME, pool=pool)

Lowering latency for read / query operations
--------------------------------------------

Some applications may need to minimize latency for read operations, including
particularly the overhead of making an API request to create or refresh a
session. :class:`~google.cloud.spanner.pool.PingingPool` is designed for such
applications, which need to configure a background thread to do the work of
keeping the sessions fresh.

Create an instance of :class:`~google.cloud.spanner.pool.PingingPool`:

.. code-block:: python

from google.cloud.spanner import Client
from google.cloud.spanner.pool import PingingPool

client = Client()
instance = client.instance(INSTANCE_NAME)
pool = PingingPool(size=10, default_timeout=5, ping_interval=300)
database = instance.database(DATABASE_NAME, pool=pool)

Set up a background thread to ping the pool's session, keeping them
from becoming stale:

.. code-block:: python

import threading

background = threading.Thread(target=pool.ping, name='ping-pool')
background.daemon = True
background.start()

Lowering latency for mixed read-write operations
------------------------------------------------

Some applications may need to minimize latency for read write operations,
including particularly the overhead of making an API request to create or
refresh a session or to begin a session's transaction.
:class:`~google.cloud.spanner.pool.TransactionPingingPool` is designed for
such applications, which need to configure a background thread to do the work
of keeping the sessions fresh and starting their transactions after use.

Create an instance of
:class:`~google.cloud.spanner.pool.TransactionPingingPool`:

.. code-block:: python

from google.cloud.spanner import Client
from google.cloud.spanner.pool import TransactionPingingPool

client = Client()
instance = client.instance(INSTANCE_NAME)
pool = TransactionPingingPool(size=10, default_timeout=5, ping_interval=300)
database = instance.database(DATABASE_NAME, pool=pool)

Set up a background thread to ping the pool's session, keeping them
from becoming stale, and ensuring that each session has a new transaction
started before it is used:

.. code-block:: python

import threading

background = threading.Thread(target=pool.ping, name='ping-pool')
background.daemon = True
background.start()
139 changes: 136 additions & 3 deletions docs/spanner/database-usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,141 @@ method:
:meth:`~google.cloud.spanner.instance.Operation.finished`
will result in an :exc`ValueError` being raised.

Non-Admin Database Usage
========================

Next Step
---------
Use a Snapshot to Read / Query the Database
-------------------------------------------

Next, learn about :doc:`session-crud-usage`.
A snapshot represents a read-only point-in-time view of the database.

Calling :meth:`~google.cloud.spanner.database.Database.snapshot` with
no arguments creates a snapshot with strong concurrency:

.. code:: python

with database.snapshot() as snapshot:
do_something_with(snapshot)

See :class:`~google.cloud.spanner.snapshot.Snapshot` for the other options
which can be passed.

.. note::

:meth:`~google.cloud.spanner.database.Database.snapshot` returns an
object intended to be used as a Python context manager (i.e., as the
target of a ``with`` statement). Use the instance, and any result
sets returned by its ``read`` or ``execute_sql`` methods, only inside
the block created by the ``with`` statement.

See :doc:`snapshot-usage` 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.

.. code:: python

with database.batch() as batch:
batch.insert_or_update(table, columns, rows)
batch.delete(table, keyset_to_delete)

.. note::

:meth:`~google.cloud.spanner.database.Database.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 :doc:`batch-usage` 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:

.. code:: python

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::

:meth:`~google.cloud.spanner.database.Database.run_in_transaction`
commits the transaction automatically if the "unit of work" function
returns without raising an exception.

.. note::

:meth:`~google.cloud.spanner.database.Database.run_in_transaction`
retries the "unit of work" function if the read / query operatoins
or the commit are aborted due to concurrent updates

See :doc:`transaction-usage` 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 :class:`~google.cloud.spanner.session.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 passing it to the :class:`~google.cloud.spanner.database.Database`
constructor:

.. code-block:: python

from google.cloud.spanner import Client
from google.cloud.spanner import FixedSizePool
client = Client()
instance = client.instance(INSTANCE_NAME)
pool = FixedSizePool(size=10, default_timeout=5)
database = instanc.database(DATABASE_NAME, pool=pool)

Note that creating a database with a pool may presume that its database
already exists, as it may need 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 :class:`~google.cloud.spanner.pool.AbstractSessionPool`:

.. code-block:: python

from google.cloud.pool 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 :doc:`advanced-session-pool-topics` for more advanced coverage of
session pools.
80 changes: 0 additions & 80 deletions docs/spanner/session-crud-usage.rst

This file was deleted.

54 changes: 0 additions & 54 deletions docs/spanner/session-implicit-txn-usage.rst

This file was deleted.

Loading