Skip to content
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
2 changes: 1 addition & 1 deletion albertson/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ def name(self):

@property
def count(self):
print self.dynamo_item
print(self.dynamo_item)
return self.dynamo_item['count']

@property
Expand Down
236 changes: 236 additions & 0 deletions albertson/base.py.bak
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
from datetime import datetime

import boto
from boto.dynamodb.exceptions import DynamoDBKeyNotFoundError

ISO_FORMAT = '%Y-%m-%dT%H:%M:%S'


class CounterPool(object):
'''
Handles schema level interactions with DynamoDB and generates individual
counters as needed.
'''
table_name = None
schema = {
'hash_key_name': 'counter_name',
'hash_key_proto_value': 'S',
}
read_units = 3
write_units = 5

def __init__(self, aws_access_key=None, aws_secret_key=None, table_name=None, schema=None, read_units=None, write_units=None, auto_create_table=True, ):
"""
:aws_access_key:
AWS Acccess Key ID with permissions to use DynamoDB
:aws_secret_key:
AWS Access Secret Key for the given Access Key ID
:table_name:
The DynamoDB table that should be used to store this pool's
counters. See http://bit.ly/DynamoDBModel for details on
DynamoDB's data model.
:schema:
The schema that will be used to create a table if one does not
already exist. See the `boto`<http://bit.ly/BotoCreateTable>_
docs for details on what's expected for a schema.
:read_units:
Read throughput to be set when a table is created. See
http://bit.ly/DynamoThoughput for details on Dynamo's provisioned
throughput system.
:write_units:
Write throughput to be set when a table is created.
:auto_create_table:
Should Albertson create a dynamodb table if the provided
`table_name` doesn't exist.
"""
self.conn = self.get_conn(aws_access_key, aws_secret_key)
self.table_name = table_name or self.table_name
self.schema = schema or self.schema
self.read_units = read_units or self.read_units
self.write_units = write_units or self.write_units
self.auto_create_table = auto_create_table

super(CounterPool, self).__init__()

def get_conn(self, aws_access_key=None, aws_secret_key=None):
'''
Hook point for overriding how the CounterPool gets its connection to
AWS.
'''
return boto.connect_dynamodb(
aws_access_key_id=aws_access_key,
aws_secret_access_key=aws_secret_key,
)

def get_table_name(self):
'''
Hook point for overriding how the CounterPool determines the table name
to use.
'''
if not self.table_name:
raise NotImplementedError(
'You must provide a table_name value or override the get_table_name method'
)
return self.table_name

def get_schema(self):
'''
Hook point for overriding how the CounterPool determines the schema
to be used when creating a missing table.
'''
if not self.schema:
raise NotImplementedError(
'You must provide a schema value or override the get_schema method'
)

return self.conn.create_schema(**self.schema)

def get_read_units(self):
'''
Hook point for overriding how the CounterPool determines the read
throughput units to set on a newly created table.
'''
return self.read_units

def get_write_units(self):
'''
Hook point for overriding how the CounterPool determines the write
throughput units to set on a newly created table.
'''
return self.write_units

def create_table(self):
'''
Hook point for overriding how the CounterPool creates a new table
in DynamooDB
'''
table = self.conn.create_table(
name=self.get_table_name(),
schema=self.get_schema(),
read_units=self.get_read_units(),
write_units=self.get_write_units(),
)

if table.status != 'ACTIVE':
table.refresh(wait_for_active=True, retry_seconds=1)

return table

def get_table(self):
'''
Hook point for overriding how the CounterPool transforms table_name
into a boto DynamoDB Table object.
'''
if hasattr(self, '_table'):
table = self._table
else:
try:
table = self.conn.get_table(self.get_table_name())
except boto.exception.DynamoDBResponseError:
if self.auto_create_table:
table = self.create_table()
else:
raise

self._table = table

return table

def create_item(self, hash_key, start=0, extra_attrs=None):
'''
Hook point for overriding how the CouterPool creates a DynamoDB item
for a given counter when an existing item can't be found.
'''
table = self.get_table()
now = datetime.utcnow().replace(microsecond=0).isoformat()
attrs = {
'created_on': now,
'modified_on': now,
'count': start,
}

if extra_attrs:
attrs.update(extra_attrs)

item = table.new_item(
hash_key=hash_key,
attrs=attrs,
)

return item

def get_item(self, hash_key, start=0, extra_attrs=None):
'''
Hook point for overriding how the CouterPool fetches a DynamoDB item
for a given counter.
'''
table = self.get_table()

try:
item = table.get_item(hash_key=hash_key)
except DynamoDBKeyNotFoundError:
item = None

if item is None:
item = self.create_item(
hash_key=hash_key,
start=start,
extra_attrs=extra_attrs,
)

return item

def get_counter(self, name, start=0):
'''
Gets the DynamoDB item behind a counter and ties it to a Counter
instace.
'''
item = self.get_item(hash_key=name, start=start)
counter = Counter(dynamo_item=item, pool=self)

return counter


class Counter(object):
'''
Interface to individual counters.
'''

def __init__(self, dynamo_item, pool):
self.dynamo_item = dynamo_item
self.pool = pool

@property
def name(self):
return self.dynamo_item['counter_name']

@property
def count(self):
print self.dynamo_item
return self.dynamo_item['count']

@property
def created_on(self):
return datetime.strptime(self.dynamo_item['created_on'], ISO_FORMAT)

@property
def modified_on(self):
return datetime.strptime(self.dynamo_item['modified_on'], ISO_FORMAT)

def refresh(self):
self.dynamo_item = self.pool.get_item(hash_key=self.name)

def increment(self, amount=1):
item = self.dynamo_item
item.add_attribute('count', amount)
item.put_attribute(
'modified_on',
datetime.utcnow().replace(microsecond=0).isoformat()
)
result = item.save(return_values='UPDATED_NEW')
item.update(result['Attributes'])

return self.count

def decrement(self, amount=1):
return self.increment(amount * -1)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python
try:
from setuptools import setup
except ImportError, err:
except ImportError as err:
from distutils.core import setup

from albertson import VERSION
Expand Down
28 changes: 28 additions & 0 deletions setup.py.bak
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env python
try:
from setuptools import setup
except ImportError, err:
from distutils.core import setup

from albertson import VERSION

setup(
name='Albertson',
version=".".join(map(str, VERSION)),
description="Easy to use, scalable, counters powered by Amazon's DynamoDB",
author="Sean O'Connor",
author_email="sean@focuslab.io",
url="https://github.com/FocusLab/Albertson",
packages=['albertson', 'albertson.dynamodb_utils'],
license="BSD",
long_description=open('README.md').read(),
install_requires=['boto>=2.2.2'],
classifiers=[
'Development Status :: 2 - Pre-Alpha',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Programming Language :: Python',
'Topic :: Software Development :: Libraries :: Python Modules',
],
test_suite='nose.collector',
)
8 changes: 4 additions & 4 deletions tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,8 +372,8 @@ def test_counter_increment(self):
pool = self.get_pool()
item = self.get_item()
counter = pool.get_counter(item.hash_key)
print item
print counter.dynamo_item
print(item)
print(counter.dynamo_item)

self.assertEqual(item, counter.dynamo_item)
old_count = counter.count
Expand All @@ -397,8 +397,8 @@ def test_counter_decrement(self):
pool = self.get_pool()
item = self.get_item()
counter = pool.get_counter(item.hash_key, start=1)
print item
print counter.dynamo_item
print(item)
print(counter.dynamo_item)

self.assertEqual(item, counter.dynamo_item)
old_count = counter.count
Expand Down
Loading