Skip to content
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

Adding helpful documentation to HappyBase package init. #1545

Merged
merged 2 commits into from
Feb 26, 2016
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
134 changes: 134 additions & 0 deletions gcloud/bigtable/happybase/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,138 @@

This package is intended to emulate the HappyBase library using
Google Cloud Bigtable as the backing store.

Differences in Public API
-------------------------

Some concepts from HBase/Thrift do not map directly to the Cloud
Bigtable API. As a result, the following instance methods and functions
could not be implemented:

* :meth:`.Connection.enable_table` - no concept of enabled/disabled
* :meth:`.Connection.disable_table` - no concept of enabled/disabled
* :meth:`.Connection.is_table_enabled` - no concept of enabled/disabled
* :meth:`.Connection.compact_table` - table storage is opaque to user
* :func:`make_row() <gcloud.bigtable.happybase.table.make_row>` - helper
needed for Thrift library
* :func:`make_ordered_row() <gcloud.bigtable.happybase.table.make_ordered_row>`
- helper needed for Thrift library
* :meth:`Table.regions() <gcloud.bigtable.happybase.table.Table.regions>`
- tables in Cloud Bigtable do not expose internal storage details
* :meth:`Table.counter_set() \
<gcloud.bigtable.happybase.table.Table.counter_set>` - method can't
be atomic, so we disable it

This comment was marked as spam.

* The ``__version__`` value for the HappyBase package is :data:`None`.
However, it's worth nothing this implementation was based off HappyBase
0.9.

This comment was marked as spam.

This comment was marked as spam.


In addition, many of the constants from :mod:`.connection` are specific
to HBase and are defined as :data:`None` in our module:

* ``COMPAT_MODES``
* ``THRIFT_TRANSPORTS``
* ``THRIFT_PROTOCOLS``
* ``DEFAULT_HOST``
* ``DEFAULT_PORT``
* ``DEFAULT_TRANSPORT``
* ``DEFAULT_COMPAT``
* ``DEFAULT_PROTOCOL``

Two of these ``DEFAULT_HOST`` and ``DEFAULT_PORT``, are even imported in
the main :mod:`happybase <gcloud.bigtable.happybase>` package.

Finally, we do not provide the ``util`` module. Though it is public in the
HappyBase library, it provides no core functionality.

API Behavior Changes
--------------------

* Since there is no concept of an enabled / disabled table, calling
:meth:`.Connection.delete_table` with ``disable=True`` can't be supported.
Using that argument will result in a warning.
* The :class:`.Connection` constructor **disables** the use of several
arguments and will print a warning if any of them are passed in as keyword
arguments. The arguments are:

* ``host``
* ``port``
* ``compat``
* ``transport``
* ``protocol``
* In order to make :class:`.Connection` compatible with Cloud Bigtable, we
add a ``cluster`` keyword argument to allow user's to pass in their own
:class:`.Cluster` (which they can construct beforehand).

For example:

.. code:: python

from gcloud.bigtable.client import Client
client = Client(project=PROJECT_ID, admin=True)
cluster = client.cluster(zone, cluster_id)
cluster.reload()

from gcloud.bigtable.happybase import Connection
connection = Connection(cluster=cluster)

* Any uses of the ``wal`` (Write Ahead Log) argument will result in a
warning as well. This includes uses in:

* :class:`.Batch` constructor
* :meth:`.Batch.put`
* :meth:`.Batch.delete`
* :meth:`Table.put() <gcloud.bigtable.happybase.table.Table.put>`
* :meth:`Table.delete() <gcloud.bigtable.happybase.table.Table.delete>`
* :meth:`Table.batch() <gcloud.bigtable.happybase.table.Table.batch>` factory
* When calling :meth:`.Connection.create_table`, the majority of HBase column
family options cannot be used. Among

* ``max_versions``
* ``compression``
* ``in_memory``
* ``bloom_filter_type``
* ``bloom_filter_vector_size``
* ``bloom_filter_nb_hashes``
* ``block_cache_enabled``
* ``time_to_live``

Only ``max_versions`` and ``time_to_live`` are availabe in Cloud Bigtable
(as
:class:`MaxVersionsGCRule <gcloud.bigtable.column_family.MaxVersionsGCRule>`
and
`MaxAgeGCRule <gcloud.bigtable.column_family.MaxAgeGCRule>`).

In addition to using a dictionary for specifying column family options,
we also accept instances of :class:`.GarbageCollectionRule` or subclasses.
* :meth:`Table.scan() <gcloud.bigtable.happybase.table.Table.scan>` no longer
accepts the following arguments (which will result in a warning):

* ``batch_size``
* ``scan_batching``
* ``sorted_columns``

* Using a HBase filter string in
:meth:`Table.scan() <gcloud.bigtable.happybase.table.Table.scan>` is
not possible with Cloud Bigtable and will result in a
:class:`TypeError <exceptions.TypeError>`. However, the method now accepts
instances of :class:`.RowFilter` and subclasses.
* :meth:`.Batch.delete` (and hence
:meth:`Table.delete() <gcloud.bigtable.happybase.table.Table.delete>`)
will fail with a :class:`ValueError <exceptions.ValueError>` when either a
row or column family delete is attempted with a ``timestamp``. This is
because the Cloud Bigtable API uses the ``DeleteFromFamily`` and
``DeleteFromRow`` mutations for these deletes, and neither of these
mutations support a timestamp.
"""

from gcloud.bigtable.happybase.batch import Batch
from gcloud.bigtable.happybase.connection import Connection
from gcloud.bigtable.happybase.connection import DEFAULT_HOST
from gcloud.bigtable.happybase.connection import DEFAULT_PORT
from gcloud.bigtable.happybase.pool import ConnectionPool
from gcloud.bigtable.happybase.pool import NoConnectionsAvailable
from gcloud.bigtable.happybase.table import Table

This comment was marked as spam.



# Values from HappyBase that we don't reproduce / are not relevant.
__version__ = None
10 changes: 4 additions & 6 deletions gcloud/bigtable/happybase/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@

_LEGACY_ARGS = frozenset(('host', 'port', 'compat', 'transport', 'protocol'))
_WARN = warnings.warn
_DISABLE_DELETE_MSG = ('The disable argument should not be used in '
'delete_table(). Cloud Bigtable has no concept '
'of enabled / disabled tables.')


def _get_cluster(timeout=None):
Expand Down Expand Up @@ -349,14 +352,9 @@ def delete_table(self, name, disable=False):
is provided for compatibility with HappyBase, but is
not relevant for Cloud Bigtable since it has no concept
of enabled / disabled tables.

:raises: :class:`ValueError <exceptions.ValueError>`
if ``disable=True``.
"""
if disable:
raise ValueError('The disable argument should not be used in '
'delete_table(). Cloud Bigtable has no concept '
'of enabled / disabled tables.')
_WARN(_DISABLE_DELETE_MSG)

This comment was marked as spam.


name = self._table_name(name)
_LowLevelTable(name, self._cluster).delete()
Expand Down
24 changes: 17 additions & 7 deletions gcloud/bigtable/happybase/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ def test_create_table_bad_value(self):
with self.assertRaises(ValueError):
connection.create_table(name, families)

def test_delete_table(self):
def _delete_table_helper(self, disable=False):
from gcloud._testing import _Monkey
from gcloud.bigtable.happybase import connection as MUT

Expand All @@ -411,20 +411,30 @@ def make_table(*args, **kwargs):

name = 'table-name'
with _Monkey(MUT, _LowLevelTable=make_table):
connection.delete_table(name)
connection.delete_table(name, disable=disable)

# Just one table would have been created.
table_instance, = tables_created
self.assertEqual(table_instance.args, (name, cluster))
self.assertEqual(table_instance.kwargs, {})
self.assertEqual(table_instance.delete_calls, 1)

def test_delete_table(self):
self._delete_table_helper()

def test_delete_table_disable(self):
cluster = _Cluster() # Avoid implicit environ check.
connection = self._makeOne(autoconnect=False, cluster=cluster)
name = 'table-name'
with self.assertRaises(ValueError):
connection.delete_table(name, disable=True)
from gcloud._testing import _Monkey
from gcloud.bigtable.happybase import connection as MUT

warned = []

def mock_warn(msg):
warned.append(msg)

with _Monkey(MUT, _WARN=mock_warn):
self._delete_table_helper(disable=True)

self.assertEqual(warned, [MUT._DISABLE_DELETE_MSG])

def test_enable_table(self):
cluster = _Cluster() # Avoid implicit environ check.
Expand Down