Skip to content

Commit faf442d

Browse files
committed
Beginning of HappyBase batch module.
1 parent a77e7fa commit faf442d

File tree

3 files changed

+189
-0
lines changed

3 files changed

+189
-0
lines changed

gcloud/bigtable/happybase/batch.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Copyright 2016 Google Inc. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Google Cloud Bigtable HappyBase batch module."""
16+
17+
18+
import datetime
19+
import warnings
20+
21+
from gcloud._helpers import _datetime_from_microseconds
22+
from gcloud.bigtable.row import TimestampRange
23+
24+
25+
_WAL_SENTINEL = object()
26+
# Assumed granularity of timestamps in Cloud Bigtable.
27+
_ONE_MILLISECOND = datetime.timedelta(microseconds=1000)
28+
_WARN = warnings.warn
29+
_WAL_WARNING = ('The wal argument (Write-Ahead-Log) is not '
30+
'supported by Cloud Bigtable.')
31+
32+
33+
class Batch(object):
34+
"""Batch class for accumulating mutations.
35+
36+
:type table: :class:`Table <gcloud.bigtable.happybase.table.Table>`
37+
:param table: The table where mutations will be applied.
38+
39+
:type timestamp: int
40+
:param timestamp: (Optional) Timestamp (in milliseconds since the epoch)
41+
that all mutations will be applied at.
42+
43+
:type batch_size: int
44+
:param batch_size: (Optional) The maximum number of mutations to allow
45+
to accumulate before committing them.
46+
47+
:type transaction: bool
48+
:param transaction: Flag indicating if the mutations should be sent
49+
transactionally or not. If ``transaction=True`` and
50+
an error occurs while a :class:`Batch` is active,
51+
then none of the accumulated mutations will be
52+
committed. If ``batch_size`` is set, the mutation
53+
can't be transactional.
54+
55+
:type wal: object
56+
:param wal: Unused parameter (Boolean for using the HBase Write Ahead Log).
57+
Provided for compatibility with HappyBase, but irrelevant for
58+
Cloud Bigtable since it does not have a Write Ahead Log.
59+
60+
:raises: :class:`TypeError <exceptions.TypeError>` if ``batch_size``
61+
is set and ``transaction=True``.
62+
:class:`ValueError <exceptions.ValueError>` if ``batch_size``
63+
is not positive.
64+
"""
65+
66+
def __init__(self, table, timestamp=None, batch_size=None,
67+
transaction=False, wal=_WAL_SENTINEL):
68+
if wal is not _WAL_SENTINEL:
69+
_WARN(_WAL_WARNING)
70+
71+
if batch_size is not None:
72+
if transaction:
73+
raise TypeError('When batch_size is set, a Batch cannot be '
74+
'transactional')
75+
if batch_size <= 0:
76+
raise ValueError('batch_size must be positive')
77+
78+
self._table = table
79+
self._batch_size = batch_size
80+
# Timestamp is in milliseconds, convert to microseconds.
81+
self._timestamp = self._delete_range = None
82+
if timestamp is not None:
83+
self._timestamp = _datetime_from_microseconds(1000 * timestamp)
84+
# For deletes, we get the very next timestamp (assuming timestamp
85+
# granularity is milliseconds). This is because HappyBase users
86+
# expect HBase deletes to go **up to** and **including** the
87+
# timestamp while Cloud Bigtable Time Ranges **exclude** the
88+
# final timestamp.
89+
next_timestamp = self._timestamp + _ONE_MILLISECOND
90+
self._delete_range = TimestampRange(end=next_timestamp)
91+
self._transaction = transaction
92+
93+
# Internal state for tracking mutations.
94+
self._row_map = {}
95+
self._mutation_count = 0
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Copyright 2016 Google Inc. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
import unittest2
17+
18+
19+
class TestBatch(unittest2.TestCase):
20+
21+
def _getTargetClass(self):
22+
from gcloud.bigtable.happybase.batch import Batch
23+
return Batch
24+
25+
def _makeOne(self, *args, **kwargs):
26+
return self._getTargetClass()(*args, **kwargs)
27+
28+
def test_constructor_defaults(self):
29+
table = object()
30+
batch = self._makeOne(table)
31+
self.assertEqual(batch._table, table)
32+
self.assertEqual(batch._batch_size, None)
33+
self.assertEqual(batch._timestamp, None)
34+
self.assertEqual(batch._delete_range, None)
35+
self.assertEqual(batch._transaction, False)
36+
self.assertEqual(batch._row_map, {})
37+
self.assertEqual(batch._mutation_count, 0)
38+
39+
def test_constructor_explicit(self):
40+
from gcloud._helpers import _datetime_from_microseconds
41+
from gcloud.bigtable.row import TimestampRange
42+
43+
table = object()
44+
timestamp = 144185290431
45+
batch_size = 42
46+
transaction = False # Must be False when batch_size is non-null
47+
48+
batch = self._makeOne(table, timestamp=timestamp,
49+
batch_size=batch_size, transaction=transaction)
50+
self.assertEqual(batch._table, table)
51+
self.assertEqual(batch._batch_size, batch_size)
52+
self.assertEqual(batch._timestamp,
53+
_datetime_from_microseconds(1000 * timestamp))
54+
55+
next_timestamp = _datetime_from_microseconds(1000 * (timestamp + 1))
56+
time_range = TimestampRange(end=next_timestamp)
57+
self.assertEqual(batch._delete_range, time_range)
58+
self.assertEqual(batch._transaction, transaction)
59+
self.assertEqual(batch._row_map, {})
60+
self.assertEqual(batch._mutation_count, 0)
61+
62+
def test_constructor_with_non_default_wal(self):
63+
from gcloud._testing import _Monkey
64+
from gcloud.bigtable.happybase import batch as MUT
65+
66+
warned = []
67+
68+
def mock_warn(msg):
69+
warned.append(msg)
70+
71+
table = object()
72+
wal = object()
73+
with _Monkey(MUT, _WARN=mock_warn):
74+
self._makeOne(table, wal=wal)
75+
76+
self.assertEqual(warned, [MUT._WAL_WARNING])
77+
78+
def test_constructor_with_non_positive_batch_size(self):
79+
table = object()
80+
batch_size = -10
81+
with self.assertRaises(ValueError):
82+
self._makeOne(table, batch_size=batch_size)
83+
batch_size = 0
84+
with self.assertRaises(ValueError):
85+
self._makeOne(table, batch_size=batch_size)
86+
87+
def test_constructor_with_batch_size_and_transactional(self):
88+
table = object()
89+
batch_size = 1
90+
transaction = True
91+
with self.assertRaises(TypeError):
92+
self._makeOne(table, batch_size=batch_size,
93+
transaction=transaction)

scripts/verify_included_modules.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
'gcloud.bigtable.client',
3434
'gcloud.bigtable.cluster',
3535
'gcloud.bigtable.column_family',
36+
'gcloud.bigtable.happybase.batch',
3637
'gcloud.bigtable.happybase.connection',
3738
'gcloud.bigtable.happybase.table',
3839
'gcloud.bigtable.row',

0 commit comments

Comments
 (0)