Skip to main content

Brokerless durable execution for Python.

Project description

Substantial

Brokerless durable execution for Python.

Substantial is part of the Metatype ecosystem. Consider checking out how this component integrates with the whole ecosystem and browse the documentation to see more examples.

What is durable execution?

Durable execution is a programming model where the state of a function is preserved across failures, restarts, or other (un)voluntary disruptions. This ensures that applications can continue execution from their last stable state without losing context or causing additional side effects. It is particularly well-suited for long-running workflows as it enables the management of complex sequences of steps, handling breaks, retries and recovery gracefully.

Substantial is designed around a replay mechanism that reconstructs the function state by re-executing historical events from stored logs. All the logic is embedded in a protocol and there is no centralized broker, allowing it to work with any backend (local files, cloud storage and databases). It aims to be an alternative for use cases that do not require the scale and complexity of Temporal or Durable Task.

Getting started

# pypi
pip install substantial
poetry add substantial

# remote master
pip install --upgrade git+https://github.com/zifeo/substantial.git
poetry add git+https://github.com/zifeo/substantial.git

# local repo/dev
poetry install
pre-commit install
protoc -I . --python_betterproto_out=. protocol/*

Workflow

def roulette():
    if random.random() < 1/6:
        raise Exception("shot!")
    return 100

def multiply(a, b):
    return a * b

@dataclass
class State:
    cancelled: bool = False

    def update(self):
        self.cancelled = True


@workflow()
async def example(c: Context):
    res = await c.save(roulette)

    await c.sleep(timedelta(seconds=10))
    res = await c.save(lambda: multiply(res, 0.5))

    n = await c.receive("by")
    res = await c.save(lambda: multiply(res, n))

    s = State(is_cancelled=False)
    c.handle("cancel", lambda: s.update())

    await c.ensure(lambda: s.cancelled):
    return res

Worker

Brokerless means that there is no active broker and all the scheduling is solely organized around files/key values. Substantial currently supports Redis, local files and s3-compatible object storages.

backend = FSBackend("./logs")
substantial = Conductor(backend)
substantial.register(example)

# run the agent in the background
agent = substantial.run()

# start the workflow
w = await substantial.start(example)

await asyncio.sleep(3)
print(await w.send("by", 2))

await asyncio.sleep(5)
print(await w.send("cancel"))

output = await w.result()
print("Final output", output) # 100

# stop the agent
agent.cancel()
await agent

API

Note: not all features are implemented/completed yet.

Primitives

save(f: Callable, compensate_with: Optional[Callable]): Any - memoize the result of a function to avoid re-execution on replay. Functions shall be idempotent as they may be called more than once in case of failure just before the value is persisted. The function can be compensated by providing its inverse effect and trigger later in the workflow with revert.

handle(event_name: str, cb: Callable): None - register a callback to be executed when a specific event is received. The callbacks are executed in the order they were received and whenever a primitive being called.

ensure(f: Callable): True - wait for the function to evaluate to true and schedule a new run when false.

Higher-level

sleep - schedule a replay after a certain amount of time.

receive - wait for the value of an event to be received.

log - TODO

datetime.now - TODO

random - TODO

uuid4 - TODO

Advanced

revert() - execute the compensations and stop the workflow.

continue_using(workflow, *args, **kwargs) - stop the current workflow and pass the context to a new one.

compact() - a key can be defined on all the primitives to avoid the infinitely growing log issue. This function will keep only the last occurrence of each of the keys.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

substantial-0.0.1.tar.gz (18.6 kB view details)

Uploaded Source

Built Distribution

substantial-0.0.1-py3-none-any.whl (22.7 kB view details)

Uploaded Python 3

File details

Details for the file substantial-0.0.1.tar.gz.

File metadata

  • Download URL: substantial-0.0.1.tar.gz
  • Upload date:
  • Size: 18.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.3 CPython/3.12.7 Darwin/23.6.0

File hashes

Hashes for substantial-0.0.1.tar.gz
Algorithm Hash digest
SHA256 8274fb9bf08969e124214b43bc197a042d4dde346cbbe099cb467d3ae75536cc
MD5 bdd53ac0e604064afba5cf3690218075
BLAKE2b-256 13b06c6e1987bc281e8334d01514335e30619d44ae5f0c8e71d11e36a83f427f

See more details on using hashes here.

File details

Details for the file substantial-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: substantial-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 22.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.3 CPython/3.12.7 Darwin/23.6.0

File hashes

Hashes for substantial-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 908a621d6b9520bf3126ae94ff1a1cf967e764619055fc60102135b94e706220
MD5 188cef82ebc627f3be7fe36364a99b0a
BLAKE2b-256 fe750b099086f1c8e3dee5cbc51495c81d69d97a623eaef4dbad6e7802dbb672

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page