Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/meadsteve/talepy

📚Coordinate "transactions" across a number of services in python
https://github.com/meadsteve/talepy

async concurrent distributed-transactions python

Last synced: 16 days ago
JSON representation

📚Coordinate "transactions" across a number of services in python

Awesome Lists containing this project

README

        

# Tale Distributed Transactions

[![Build Status](https://travis-ci.org/meadsteve/talepy.svg?branch=master)](https://travis-ci.org/meadsteve/talepy)

## What?
Tale is a small library to help write a "distributed transaction like"
object across a number of services. It's loosely based on the saga pattern.
A good intro is available on the couchbase blog:
https://blog.couchbase.com/saga-pattern-implement-business-transactions-using-microservices-part/

## Installation
```bash
pipenv install talepy
```

## Example Usage
An example use case of this would be some holiday booking software broken
down into a few services.

Assuming we have the following services: Flight booking API, Hotel booking API,
and a Customer API.

We'd write the following steps:

```python
from talepy.steps import Step

class DebitCustomerBalance(Step):

def __init__(self):
self.payment_client= {}

def execute(self, state):
state['payment_id'] = self.payment_client.bill(state.customer_id, state.payment_amount)
return state

def compensate(self, state):
self.payment_client.refund(state['payment_id'])

```

and so on for any of the steps needed. Then in whatever is handling the user's
request a distributed transaction can be built:

```python
from talepy import run_transaction

run_transaction(
steps=[
DebitCustomerBalance(),
BookFlight(),
BookHotel(),
EmailCustomerDetailsOfBooking()
],
starting_state={}
)
```

If any step along the way fails then the compensate method on each step
is called in reverse order until everything is undone.

### Steps as Lambdas

For some cases you may not want to create a class for the step. Lambdas can be used directly
instead. Extending the previous example:

```python
from talepy import run_transaction

run_transaction(
steps=[
DebitCustomerBalance(),
BookFlight(),
lambda _: print("LOG -- The flight has been booked"),
BookHotel(),
EmailCustomerDetailsOfBooking()
],
starting_state={}
)
```

This new print statement will now execute following a success in `BookFlight`.

It's also possible to implement compensations by adding another lambda as a tuple pair:

```python
from talepy import run_transaction

run_transaction(
steps=[
DebitCustomerBalance(),
BookFlight(),
(
lambda _: print("LOG -- The flight has been booked"),
lambda _: print("LOG -- ┌[ ಠ ▃ ಠ ]┐ something went wrong")
),
BookHotel(),
EmailCustomerDetailsOfBooking()
],
starting_state={}
)
```

### Automatic retries

You may also want to try a step a few times before giving up. A convinence
function is provided to help out with this. Starting with the initial example.
If the hotel booking step is a bit unreliable and we want to try it 3 times:

```python
from talepy import run_transaction
from talepy.retries import attempt_retries

run_transaction(
steps=[
DebitCustomerBalance(),
BookFlight(),
attempt_retries(BookHotel(), times=2),
EmailCustomerDetailsOfBooking()
],
starting_state={}
)
```

The book hotel step will now be executed 3 times before the transaction is aborted. Once
all these attempts fail the normal compensation logic will be applied.

### Async

If you want to make use of `async` in your steps you will need to import `run_transaction`
from `talepy.async_transactions`. This can be awaited on and allows async steps and
compensations. In addition you can also `run_concurrent_transaction` where all
steps will be executed concurrently. The downside here is that the ordering
of the steps isn't guaranteed. This means all steps receive the same starting state.

#### example
```python
from talepy.async_transactions import run_transaction
from talepy.steps import Step

class AsyncBookFlight(Step):

async def execute(self, state):
# do something
return state

async def compensate(self, state):
# revert something
pass

await run_transaction(
step_defs=[
DebitCustomerBalance(),
AsyncBookFlight(),
EmailCustomerDetailsOfBooking()
],
starting_state={}
)
```
#### Concurrent example
```python
from talepy.async_transactions import run_concurrent_transaction
from talepy.steps import Step

class AsyncBookFlight(Step):

async def execute(self, state):
# do something
return state

async def compensate(self, state):
# revert something
pass

await run_concurrent_transaction(
steps=[
DebitCustomerBalance(),
AsyncBookFlight(),
EmailCustomerDetailsOfBooking()
],
starting_state={}
)
```

## Testing / Development
TODO