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鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
question: Any chance for transaction support? #53
Comments
No plans to add support right now, but would be nice to have of course. sadly, transactions seem to be unsupported by dynalite (or alternative dynamodb emulators) which will make testing this difficult (basically requiring "real" dynamodb). We should probably discuss API design before you dive too deep. At a first glance, I think Maybe the signature should be |
@stupoid do you use either transaction mechanism already? |
Calling on users who want transactions... please let yourselves known, e.g. by 馃憤馃徔 on the OP 猬嗭笍 |
Hi @ojii, how are you? I'm actually also interested in transaction support. If you are still interested, I have the time to collaborate with this. 馃槃 And about the API design, I liked the idea of building similar to I just want to hear your thoughts about and if possible collaborate with this. 馃槂 |
looks like localstack is a wrapper around dynamodb-local fixing some of its shortcomings. This seems like a reasonable thing to test against. So a good first step to adding transaction support would be to update our CI setup to also test against localstack. We can then either use feature detection or flag in the tests to only run transaction tests against implementations that support it. Once we have something in place that we can test against, we can think about a good API for this. |
@ojii Nice! I think we can start with the CI. How can we do that? Should I open a pull request adding a new step on the pipeline to run tests with localstack? 馃 |
yes please |
not quite. Conditions are also built using (different) methods on
|
well, since you already did the PR I think it is reasonable to add localstack to the test matrix. |
Hi @ojii, thanks for merging the PR. So, following your ideas I started to sketch a solution here and before I open another pull request I want to share with you. @dataclass(frozen=True)
class Put:
item: Item
condition: Optional[Condition] = None
def to_request_payload(self, table: TableName) -> Dict[str, Any]:
dynamo_item = py2dy(self.item)
if not dynamo_item:
raise EmptyItem()
payload: Dict[str, Any] = {
"TableName": table,
"Item": dynamo_item,
}
if self.condition:
params = Parameters()
payload["ConditionExpression"] = self.condition.encode(params)
payload.update(params.to_request_payload())
return payload
@dataclass(frozen=True)
class Update:
key: Item
expression: Expression
condition: Optional[Condition] = None
def to_request_payload(self, table: TableName) -> Dict[str, Any]:
params = Parameters()
expression = self.expression.encode(params)
if not expression:
raise EmptyItem()
payload: Dict[str, Any] = {
"TableName": table,
"UpdateExpression": expression,
"Key": py2dy(self.key),
}
if self.condition:
payload["ConditionExpression"] = self.condition.encode(params)
payload.update(params.to_request_payload())
return payload
@dataclass(frozen=True)
class Delete:
key: Item
condition: Optional[Condition] = None
def to_request_payload(self, table: TableName) -> Dict[str, Any]:
dynamo_item = py2dy(self.key)
if not dynamo_item:
raise EmptyItem()
payload: Dict[str, Any] = {
"TableName": table,
"Key": dynamo_item,
}
if self.condition:
params = Parameters()
payload["ConditionExpression"] = self.condition.encode(params)
payload.update(params.to_request_payload())
return payload
@dataclass(frozen=True)
class TransactWriteItems:
items_to_put: Optional[List[Put]] = None
items_to_update: Optional[List[Update]] = None
items_to_delete: Optional[List[Update]] = None
def to_request_payload(self, table: TableName) -> List[Dict[str, Any]]:
payload: List[Dict[str, Any]] = []
if self.items_to_put:
payload.extend(
{"Put": item.to_request_payload(table)} for item in self.items_to_put
)
if self.items_to_update:
payload.extend(
{"Update": item.to_request_payload(table)}
for item in self.items_to_update
)
if self.items_to_delete:
payload.extend(
{"Delete": item.to_request_payload(table)}
for item in self.items_to_delete
)
return payload And in the client: async def transact_write_items(
self,
table: TableName,
request: TransactWriteItems,
*
request_token: Optional[str] = None,
):
payload = {
"ClientRequestToken": request_token,
"TransactItems": request.to_request_payload(table=table),
}
response = await self.send_request(action="TransactWriteItems", payload=payload)
return response I think it would be better to have a return object of the operation and also think about ConditionCheck. But before thinking about that I just want to know if this design is something that can work for you. |
I think that's quite close to what I had in mind. I don't think we need The The method itself should guard against as many of the limitations of this API as we can easily do client side (minimum one item, maximum 25, ...) |
Nice @ojii! 馃榿 Good to know that we are on the right track. So I will know move to open a PR and we can continuing discussing there. What do you think? |
sounds good. makes it easier to comment on specific lines/pieces of code too |
implemented in 22.8. |
I'm wondering if there are any plans to support TransactWriteItems or just Transactions in general.
I can probably work on this if/when I have more time 馃
The text was updated successfully, but these errors were encountered: