Commits

Jean-Tiare Le Bigot committed 873b858

add support for ConsistentRead in BatchGetItem

  • Participants
  • Parent commits 9d93304

Comments (0)

Files changed (9)

     - bundle sqlite store
     - add config param to switch
 - ``clean_boto_patch`` to restore original ``boto.dynamodb`` behavior
+- allow ``ConsistentRead`` on a per-table basis for ``BatchGetItem``
 
 Removal
 -------
  - regular network based entry-point with 1:1 correspondance with stock DynamoDB
  - **embeded entry-point** with seamless boto intergration 1, ideal to avoid spinning yet another server.
 
+Installation
+============
+
+::
+
+    $ pip install ddbmock
+
+
+Developing
+==========
+
+::
+
+    $ hg clone ssh://hg@bitbucket.org/Ludia/dynamodb-mock
+    $ pip install nose nosexcover coverage mock webtests boto
+    $ python setup.py develop
+    $ nosetests # --no-coverage to run boto integration tests too
+
+
 What is/will ddbmock be useful for ?
 ====================================
 

ddbmock/database/comparison.py

 # -*- coding: utf-8 -*-
 
+import sys
 from decimal import Decimal
 
 # Types and syntax are already checked
 # syntax is expected allright cf input validators
 
+# This module is designed to be used reflexively by ``Scan`` and ``Query``
+# implementation through ``getattr``. This helps to keep the code lean in these
+# place while allowing to add new operators easily as Amazon implements new ones.
+# List of valid Operators are specified in Onctuous validators for each API calls.
+
+# Helpers
+
 def _coerce(typename, value):
     if typename == 'S' or typename == 'B':
         return value
     value = _coerce(typename, value)
     return typename, value
 
-#Types restrictions are performed by the input validators
-
 
 # Common comparison operators
 
     return target in rules
 
 # workaround to use a function with a built-in name
-import sys
-setattr(sys.modules[__name__], 'in', in_test)
+setattr(sys.modules[__name__], 'in', in_test)

ddbmock/database/db.py

     def get_batch(self, batch):
         ret = defaultdict(dict)
 
-        for tablename, keys in batch.iteritems():
-            fields = keys[u'AttributesToGet']
+        for tablename, batch in batch.iteritems():
+            base_capacity = 1 if batch[u'ConsistentRead'] else 0.5
+            fields = batch[u'AttributesToGet']
             table = self.get_table(tablename)
             units = ItemSize(0)
             items = []
-            for key in keys[u'Keys']:
+            for key in batch[u'Keys']:
                 item = table.get(key, fields)
                 if item:
                     units += item.get_size().as_units()
                     items.append(item)
             push_read_throughput(tablename, 0.5*units)
             ret[tablename][u'Items'] = items
-            ret[tablename][u'ConsumedCapacityUnits'] = 0.5*units  # eventually consistent read
+            ret[tablename][u'ConsumedCapacityUnits'] = base_capacity*units
 
         return ret
 

ddbmock/validators/batch_get_item.py

 # -*- coding: utf-8 -*-
 
-from .types import table_name, Required, get_key_schema, attributes_to_get_schema
+from .types import table_name, Required, get_key_schema, attributes_to_get_schema,consistent_read
 
 post = {
     u"RequestItems": {
         table_name: {
             u"Keys": [get_key_schema],
             Required(u'AttributesToGet', []): attributes_to_get_schema,
+            Required(u'ConsistentRead', False): consistent_read,
         },
     },
 }

ddbmock/validators/get_item.py

     u'TableName': table_name,
     u'Key': get_key_schema,
     Required(u'AttributesToGet', []): attributes_to_get_schema,
-    Required(u'ConsistentRead', False): consistent_read,  #FIXME: handle default
+    Required(u'ConsistentRead', False): consistent_read,
 }
 
     'pyramid',
     'waitress',
-    'onctuous',
+    'onctuous >= 0.5.1',
 
     'setuptools >= 0.6b1',
 ]

tests/functional/boto/test_batch_get_item.py

 RK_VALUE4 = u'Waldo-4'
 RK_VALUE5 = u'Waldo-5'
 
+# TODO: add support for ConsistentRead in tests once Boto supports it
 
 ITEM1 = {
     TABLE_HK_NAME: {TABLE_HK_TYPE: HK_VALUE1},

tests/functional/pyramid/test_batch_get_item.py

         res = self.app.post_json('/', request, HEADERS, status=200)
         self.assertEqual(expected, json.loads(res.body))
         self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type'])
+
+    def test_batch_get_item_filter_one_consistent(self):
+        from ddbmock.database.db import dynamodb
+
+        request = {
+            u"RequestItems": {
+                TABLE_NAME1: {
+                    u"Keys": [
+                        {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}},
+                        {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE1}, u"RangeKeyElement": {TABLE_RK_TYPE: RK_VALUE1}},
+                    ],
+                    u"AttributesToGet": [u"relevant_data"],
+                    u"ConsistentRead": True,
+                },
+                TABLE_NAME2: {
+                    u"Keys": [
+                        {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE3}},
+                    ],
+                },
+            }
+        }
+
+        expected = {
+            "Responses": {
+                "Table-HR": {
+                    "Items": [
+                        {"relevant_data": {"S": "tata"}},
+                        {"relevant_data": {"S": "tata"}},
+                    ],
+                    "ConsumedCapacityUnits": 2.0
+                },
+                "Table-H": {
+                    "Items": [
+                        {"relevant_data": {"S": "tutu"}, "hash_key": {"N": "789"}, "range_key": {"S": "Waldo-5"}},
+                    ],
+                    "ConsumedCapacityUnits": 0.5
+                }
+            }
+        }
+
+        # Protocol check
+        res = self.app.post_json('/', request, HEADERS, status=200)
+        self.assertEqual(expected, json.loads(res.body))
+        self.assertEqual('application/x-amz-json-1.0; charset=UTF-8', res.headers['Content-Type'])
+
+