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

add support for PAY_PER_REQUEST BillingMode #72

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
0.9.5 - 2019-08-14
##################

* Add support for PAY_PER_REQUEST billing mode.

0.9.3 - 2019.04.30
##################

Expand Down
4 changes: 4 additions & 0 deletions dynamorm/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ class MissingTableAttribute(DynamoTableException):
"""A required attribute is missing"""


class InvalidTableAttribute(DynamoTableException):
"""An attribute has an invalid value"""


class InvalidSchemaField(DynamoTableException):
"""A field provided does not exist in the schema"""

Expand Down
75 changes: 53 additions & 22 deletions dynamorm/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,27 @@
The attributes you define on your inner ``Table`` class map to underlying boto data structures. This mapping is
expressed through the following data model:

========= ======== ==== ===========
Attribute Required Type Description
========= ======== ==== ===========
name True str The name of the table, as stored in Dynamo.
========= ======== ==== ===========
Attribute Required Type Description
========= ======== ==== ===========
name True str The name of the table, as stored in Dynamo.

hash_key True str The name of the field to use as the hash key.
hash_key True str The name of the field to use as the hash key.
It must exist in the schema.

range_key False str The name of the field to use as the range_key, if one is used.
range_key False str The name of the field to use as the range_key, if one is used.
It must exist in the schema.

read True int The provisioned read throughput.
read Cond int The provisioned read throughput. Required for 'PROVISIONED' billing_mode (default).

write True int The provisioned write throughput.
write Cond int The provisioned write throughput. Required for 'PROVISIONED' billing_mode (default).

stream False str The stream view type, either None or one of:
'NEW_IMAGE'|'OLD_IMAGE'|'NEW_AND_OLD_IMAGES'|'KEYS_ONLY'
billing_mode True str The billing mode. One of: 'PROVISIONED'|'PAY_PER_REQUEST'

========= ======== ==== ===========
stream False str The stream view type, either None or one of:
'NEW_IMAGE'|'OLD_IMAGE'|'NEW_AND_OLD_IMAGES'|'KEYS_ONLY'

========= ======== ==== ===========
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI is complaining about malformed RST here:

Warning, treated as error:
/home/travis/build/NerdWalletOSS/dynamorm/dynamorm/table.py:docstring of dynamorm.table:13:Malformed table.
Text in column margin in table line 7.



Indexes
Expand Down Expand Up @@ -68,6 +70,7 @@ class DynamoCommon3(object):
range_key = None
read = None
write = None
billing_mode = 'PROVISIONED'

def __init__(self):
for attr in self.REQUIRED_ATTRS:
Expand Down Expand Up @@ -96,6 +99,9 @@ def as_schema(name, key_type):
@property
def provisioned_throughput(self):
"""Return an appropriate ProvisionedThroughput, based on our attributes"""
if self.billing_mode != 'PROVISIONED':
return None

return {
'ReadCapacityUnits': self.read,
'WriteCapacityUnits': self.write
Expand All @@ -119,6 +125,7 @@ def lookup_by_type(cls, index_type):
def __init__(self, table, schema):
self.table = table
self.schema = schema
self.billing_mode = table.billing_mode

super(DynamoIndex3, self).__init__()

Expand Down Expand Up @@ -163,7 +170,8 @@ class DynamoGlobalIndex3(DynamoIndex3):
@property
def index_args(self):
args = super(DynamoGlobalIndex3, self).index_args
args['ProvisionedThroughput'] = self.provisioned_throughput
if self.billing_mode == 'PROVISIONED':
args['ProvisionedThroughput'] = self.provisioned_throughput
return args


Expand Down Expand Up @@ -340,21 +348,28 @@ def create_table(self, wait=True):

:param bool wait: If set to True, the default, this call will block until the table is created
"""
if not self.read or not self.write:
raise MissingTableAttribute("The read/write attributes are required to create a table")
if self.billing_mode not in ('PROVISIONED', 'PAY_PER_REQUEST'):
raise InvalidTableAttribute("valid values for billing_mode are: PROVISIONED|PAY_PER_REQUEST")

if self.billing_mode == 'PROVISIONED' and (not self.read or not self.write):
raise MissingTableAttribute("The read/write attributes are required to create "
"a table when billing_mode is 'PROVISIONED'")

index_args = collections.defaultdict(list)
extra_args = collections.defaultdict(list)
for index in six.itervalues(self.indexes):
index_args[index.ARG_KEY].append(index.index_args)
extra_args[index.ARG_KEY].append(index.index_args)

if self.billing_mode == 'PROVISIONED':
extra_args['ProvisionedThroughput'] = self.provisioned_throughput

log.info("Creating table %s", self.name)
table = self.resource.create_table(
TableName=self.name,
KeySchema=self.key_schema,
AttributeDefinitions=self.attribute_definitions,
ProvisionedThroughput=self.provisioned_throughput,
StreamSpecification=self.stream_specification,
**index_args
BillingMode=self.billing_mode,
**extra_args
)
if wait:
log.info("Waiting for table creation...")
Expand Down Expand Up @@ -430,8 +445,19 @@ def do_update(**kwargs):

wait_for_active()

billing_args = {}

# check if we're going to change our billing mode
current_billing_mode = table.billing_mode_summary['BillingMode']
if self.billing_mode != current_billing_mode:
log.info("Updating billing mode on table %s (%s -> %s)",
self.name,
current_billing_mode,
self.billing_mode)
billing_args['BillingMode'] = self.billing_mode

# check if we're going to change our capacity
if (self.read and self.write) and \
if (self.billing_mode == 'PROVISIONED' and self.read and self.write) and \
(self.read != table.provisioned_throughput['ReadCapacityUnits'] or
self.write != table.provisioned_throughput['WriteCapacityUnits']):

Expand All @@ -443,7 +469,10 @@ def do_update(**kwargs):
if k.endswith('Units')
),
self.provisioned_throughput)
do_update(ProvisionedThroughput=self.provisioned_throughput)
billing_args['ProvisionedThroughput'] = self.provisioned_throughput

if billing_args:
do_update(**billing_args)
return self.update_table()

# check if we're going to modify the stream
Expand Down Expand Up @@ -475,7 +504,9 @@ def do_update(**kwargs):
for index in six.itervalues(self.indexes):
if index.name in existing_indexes:
current_capacity = existing_indexes[index.name]['ProvisionedThroughput']
if (index.read and index.write) and \
update_args = {}

if (index.billing_mode == 'PROVISIONED' and index.read and index.write) and \
(index.read != current_capacity['ReadCapacityUnits'] or
index.write != current_capacity['WriteCapacityUnits']):

Expand All @@ -484,7 +515,7 @@ def do_update(**kwargs):

do_update(GlobalSecondaryIndexUpdates=[{
'Update': {
'IndexName': index['IndexName'],
'IndexName': index.name,
'ProvisionedThroughput': index.provisioned_throughput
}
}])
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name='dynamorm',
version='0.9.3',
version='0.9.5',
description='DynamORM is a Python object & relation mapping library for Amazon\'s DynamoDB service.',
long_description=long_description,
author='Evan Borgstrom',
Expand All @@ -14,7 +14,7 @@
license='Apache License Version 2.0',
install_requires=[
'blinker>=1.4,<2.0',
'boto3>=1.3,<2.0',
'boto3>=1.9.54,<2.0',
'six',
],
extras_require={
Expand Down