1414
1515
1616from memoized_property import memoized_property
17- import boto .dynamodb2
18- from boto .dynamodb2 .table import Table
19- from boto .dynamodb2 .exceptions import ConditionalCheckFailedException
17+ import boto3
18+ from botocore .exceptions import ClientError
2019import os
2120from datalake .common .errors import InsufficientConfiguration
2221import logging
@@ -42,43 +41,55 @@ def from_config(cls):
4241
4342 def _prepare_connection (self , connection ):
4443 self .logger .info ("Preparing connection..." )
45- region = os .environ .get ('AWS_REGION' )
44+ # boto2 defaulted to us-east-1, maintaining consistency across all AWS services
45+ region = os .environ .get ('AWS_REGION' , 'us-east-1' )
4646 if connection :
47+ # When connection is provided from outside, we need to ensure _client is set
4748 self ._connection = connection
48- elif region :
49- self ._connection = boto .dynamodb2 .connect_to_region (region )
49+
50+ # Check if _connection has a client attribute (added in our tests)
51+ # or create a new client if it doesn't
52+ if hasattr (connection , 'client' ):
53+ self ._client = connection .client
54+ else :
55+ self ._client = boto3 .client ('dynamodb' , region_name = region )
5056 else :
51- msg = 'Please provide a connection or configure a region'
52- raise InsufficientConfiguration ( msg )
57+ self . _connection = boto3 . resource ( 'dynamodb' , region_name = region )
58+ self . _client = boto3 . client ( 'dynamodb' , region_name = region )
5359
5460 @memoized_property
5561 def _table (self ):
56- return Table (self .table_name , connection = self . _connection )
57-
62+ return self . _connection . Table (self .table_name )
63+
5864 @memoized_property
5965 def _latest_table (self ):
60- return Table (self .latest_table_name , connection = self . _connection )
66+ return self . _connection . Table (self .latest_table_name )
6167
6268 def store (self , record ):
6369 try :
64- self ._table .put_item (data = record )
65- except ConditionalCheckFailedException :
66- # Tolerate duplicate stores
67- pass
70+ self ._table .put_item (Item = record )
71+ except ClientError as e :
72+ if e .response ['Error' ]['Code' ] == 'ConditionalCheckFailedException' :
73+ # Tolerate duplicate stores
74+ pass
75+ else :
76+ raise
6877 if self .latest_table_name :
6978 self .store_latest (record )
7079
7180 def update (self , record ):
72- self ._table .put_item (data = record , overwrite = True )
81+ self ._table .put_item (Item = record )
7382
7483 def store_latest (self , record ):
7584 """
7685 Record must utilize AttributeValue syntax
7786 for the conditional put.
7887 """
79- condition_expression = " attribute_not_exists(what_where_key) OR metadata.#metadata_start <= :new_start"
88+ # Boto3 requires different parameter naming: condition_expression -> ConditionExpression
89+ condition_expression = "attribute_not_exists(what_where_key) OR metadata.#metadata_start <= :new_start"
90+
8091 expression_attribute_values = {
81- ':new_start' : {'N' : str (record ['metadata' ]['start' ])}
92+ ':new_start' : {'N' : str (record ['metadata' ]['start' ])} # Must use typed dict here
8293 }
8394
8495 # aliases for DynamoDB reserved names.
@@ -132,17 +143,21 @@ def store_latest(self, record):
132143 }
133144 self .logger .info (f"Attempting to store record: { record } " )
134145 try :
135- self ._connection .put_item (
136- table_name = self .latest_table_name ,
137- item = record ,
138- condition_expression = condition_expression ,
139- expression_attribute_names = expression_attribute_names ,
140- expression_attribute_values = expression_attribute_values ,
146+ self ._client .put_item (
147+ TableName = self .latest_table_name ,
148+ Item = record ,
149+ ConditionExpression = condition_expression ,
150+ ExpressionAttributeNames = expression_attribute_names ,
151+ ExpressionAttributeValues = expression_attribute_values ,
141152 )
142153 self .logger .info ("Record stored successfully." )
143- except ConditionalCheckFailedException :
144- self .logger .debug (f"Condition not met for record { record } ,"
145- "no operation was performed." )
146- except Exception as e :
147- self .logger .error (f"Error occurred while attempting { record } : { str (e )} " )
154+ except ClientError as e :
155+ # Handle conditional check failures (expected when record is older than existing one)
156+ if e .response ['Error' ]['Code' ] == 'ConditionalCheckFailedException' :
157+ self .logger .debug (f"Condition not met for record { record } , "
158+ "no operation was performed." )
159+ else :
160+ # All other AWS/boto3 errors - log and re-raise
161+ self .logger .error (f"Error occurred while attempting { record } : { str (e )} " )
162+ raise
148163
0 commit comments