我正在尝试使用Python Boto 2.3.0更新原子计数计数器,但是找不到该操作的文档。
似乎没有直接接口,所以我尝试使用layer1接口进行“原始”更新,但我甚至无法完成简单的更新。
我尝试了以下变化,但都没有运气
dynoConn.update_item(INFLUENCER_DATA_TABLE,
{'HashKeyElement': "9f08b4f5-d25a-4950-a948-0381c34aed1c"},
{'new': {'Value': {'N':"1"}, 'Action': "ADD"}})
dynoConn.update_item('influencer_data',
{'HashKeyElement': "9f08b4f5-d25a-4950-a948-0381c34aed1c"},
{'new': {'S' :'hello'}})
dynoConn.update_item("influencer_data",
{"HashKeyElement": "9f08b4f5-d25a-4950-a948-0381c34aed1c"},
{"AttributesToPut" : {"new": {"S" :"hello"}}})
它们都会产生同样的错误:
File "/usr/local/lib/python2.6/dist-packages/boto-2.3.0-py2.6.egg/boto/dynamodb/layer1.py", line 164, in _retry_handler
data)
boto.exception.DynamoDBResponseError: DynamoDBResponseError: 400 Bad Request
{u'Message': u'Expected null', u'__type': u'com.amazon.coral.service#SerializationException'}
我还调查了API文档here,但它们非常简洁。
我已经做了很多搜索和摆弄,我唯一剩下的就是使用PHP API并深入研究代码以找到它“格式化”JSON主体的位置,但这有点痛苦。请救我脱离这种痛苦!
答案 0 :(得分:12)
对不起,我误解了你在找什么。您可以通过layer2完成此操作,尽管有一个小错误需要解决。这是一些第2层代码:
>>> import boto
>>> c = boto.connect_dynamodb()
>>> t = c.get_table('counter')
>>> item = t.get_item('counter')
>>> item
{u'id': 'counter', u'n': 1}
>>> item.add_attribute('n', 20)
>>> item.save()
{u'ConsumedCapacityUnits': 1.0}
>>> item # Here's the bug, local Item is not updated
{u'id': 'counter', u'n': 1}
>>> item = t.get_item('counter') # Refetch item just to verify change occurred
>>> item
{u'id': 'counter', u'n': 21}
这导致与您在Layer1代码中执行的相同的线上请求,如以下调试输出所示。
2012-04-27 04:17:59,170 foo [DEBUG]:StringToSign:
POST
/
host:dynamodb.us-east-1.amazonaws.com
x-amz-date:Fri, 27 Apr 2012 11:17:59 GMT
x-amz-security- token:<removed> ==
x-amz-target:DynamoDB_20111205.UpdateItem
{"AttributeUpdates": {"n": {"Action": "ADD", "Value": {"N": "20"}}}, "TableName": "counter", "Key": {"HashKeyElement": {"S": "counter"}}}
如果你想避免初始的GetItem调用,你可以这样做:
>>> import boto
>>> c = boto.connect_dynamodb()
>>> t = c.get_table('counter')
>>> item = t.new_item('counter')
>>> item.add_attribute('n', 20)
>>> item.save()
{u'ConsumedCapacityUnits': 1.0}
如果该项目已存在,则会更新该项目,如果该项目尚不存在,则会创建该项目。
答案 1 :(得分:5)
对于那些寻找答案的人我已经找到了答案。 首先重要说明,我目前还没有意识到目前发生了什么,要获得一个我必须执行以下操作的第1层实例:
import boto
AWS_ACCESS_KEY=XXXXX
AWS_SECRET_KEY=YYYYY
dynoConn = boto.connect_dynamodb(AWS_ACCESS_KEY, AWS_SECRET_KEY)
dynoConnLayer1 = boto.dynamodb.layer1.Layer1(AWS_ACCESS_KEY, AWS_SECRET_KEY)
基本上实例化第2层第一层然后第1层。 也许我做了一些愚蠢的事情,但在这一点上我很高兴让它工作.... 我稍后会对细节进行排序。然后......实际进行原子更新调用:
dynoConnLayer1.update_item("influencer_data",
{"HashKeyElement":{"S":"9f08b4f5-d25a-4950-a948-0381c34aed1c"}},
{"direct_influence":
{"Action":"ADD","Value":{"N":"20"}}
}
);
在上面的示例中注意,Dynamo将ADD 20添加到当前值,并且此操作将是原子意义,这意味着其他操作发生在&#34;同一时间&#34;将正确&#34;安排&#34;在执行此操作之前将新值设置为+20或之后发生。无论哪种方式,都可以达到预期的效果。
确保在layer1连接的实例上执行此操作,因为layer2会抛出错误,因为它需要一组不同的参数类型。
多数人都有!!!!大家都知道,我用PHP SDK来解决这个问题。安装和设置的时间非常短。当您进行呼叫时,调试数据实际上会显示HTTP请求主体的格式,因此您可以在示例后复制/建模您的layer1参数。这是我用来在PHP中进行原子更新的代码:
<?php
// Instantiate the class
$dynamodb = new AmazonDynamoDB();
$update_response = $dynamodb->update_item(array(
'TableName' => 'influencer_data',
'Key' => array(
'HashKeyElement' => array(
AmazonDynamoDB::TYPE_STRING=> '9f08b4f5-d25a-4950-a948-0381c34aed1c'
)
),
'AttributeUpdates' => array(
'direct_influence' => array(
'Action' => AmazonDynamoDB::ACTION_ADD,
'Value' => array(
AmazonDynamoDB::TYPE_NUMBER => '20'
)
)
)
));
// status code 200 indicates success
print_r($update_response);
?>
希望这会帮助其他人直到Boto layer2界面赶上......或者有人只是想知道如何在level2中做到这一点: - )
答案 2 :(得分:0)
DynamoDB中没有用于原子计数器的高级功能。但是,您可以使用条件写入功能实现原子计数器。例如,假设您有一个带有字符串哈希键的表,就像这样。
>>> import boto
>>> c = boto.connect_dynamodb()
>>> schema = s.create_schema('id', 's')
>>> counter_table = c.create_table('counter', schema, 5, 5)
现在,您可以向该表写入一个项目,该项目包含名为“n”的属性,其值为零。
>>> n = 0
>>> item = counter_table.new_item('counter', {'n': n})
>>> item.put()
现在,如果我想更新计数器的值,我会执行一个条件写操作,如果它的当前值与我对它的当前值的看法一致,那么它会将'n'的值加到1。
>>> n += 1
>>> item['n'] = n
>>> item.put(expected_value={'n': n-1})
这会将项目中“n”的值设置为1,但前提是DynamoDB中的当前值为零。如果该值已被其他人增加,则写入将失败,然后我需要通过本地计数器递增并再次尝试。
这有点复杂,但所有这些都可以包含在一些代码中,以使其更易于使用。我在SimpleDB上做了类似的事情,你可以在这里找到:
http://www.elastician.com/2010/02/stupid-boto-tricks-2-reliable-counters.html
我应该尝试更新该示例以使用DynamoDB
答案 3 :(得分:0)
我不确定这是一个真正的原子计数器,因为当你增加1的值时,另一个调用可以将数字增加1,这样当你“获得”该值时,它不是值你会期待的。
例如,将代码放入garnaat,标记为已接受的答案,我看到当你把它放在一个帖子中时,它不起作用:
class ThreadClass(threading.Thread):
def run(self):
conn = boto.dynamodb.connect_to_region(aws_access_key_id=os.environ['AWS_ACCESS_KEY'], aws_secret_access_key=os.environ['AWS_SECRET_KEY'], region_name='us-east-1')
t = conn.get_table('zoo_keeper_ids')
item = t.new_item('counter')
item.add_attribute('n', 1)
r = item.save() #- Item has been atomically updated!
# Uh-Oh! The value may have changed by the time "get_item" is called!
item = t.get_item('counter')
self.counter = item['n']
logging.critical('Thread has counter: ' + str(self.counter))
tcount = 3
threads = []
for i in range(tcount):
threads.append(ThreadClass())
# Start running the threads:
for t in threads:
t.start()
# Wait for all threads to complete:
for t in threads:
t.join()
#- Now verify all threads have unique numbers:
results = set()
for t in threads:
results.add(t.counter)
print len(results)
print tcount
if len(results) != tcount:
print '***Error: All threads do not have unique values!'
else:
print 'Success! All threads have unique values!'
注意:如果您希望它真正起作用,请将代码更改为:
def run(self):
conn = boto.dynamodb.connect_to_region(aws_access_key_id=os.environ['AWS_ACCESS_KEY'], aws_secret_access_key=os.environ['AWS_SECRET_KEY'], region_name='us-east-1')
t = conn.get_table('zoo_keeper_ids')
item = t.new_item('counter')
item.add_attribute('n', 1)
r = item.save(return_values='ALL_NEW') #- Item has been atomically updated, and you have the correct value without having to do a "get"!
self.counter = str(r['Attributes']['n'])
logging.critical('Thread has counter: ' + str(self.counter))
希望这有帮助!
答案 4 :(得分:0)
您要在dynamodb中增加一个值,然后可以使用以下方法来实现:
import boto3
import json
import decimal
class DecimalEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, decimal.Decimal):
if o % 1 > 0:
return float(o)
else:
return int(o)
return super(DecimalEncoder, self).default(o)
ddb = boto3.resource('dynamodb')
def get_counter():
table = ddb.Table(TableName)
try:
response = table.update_item(
Key={
'haskey' : 'counterName'
},
UpdateExpression="set currentValue = currentValue + :val",
ExpressionAttributeValues={
':val': decimal.Decimal(1)
},
ReturnValues="UPDATED_NEW"
)
print("UpdateItem succeeded:")
except Exception as e:
raise e
print(response["Attributes"]["currentValue" ])
此实现需要一个额外的计数器表,该表将为您保留最后使用的值。