Skip to main content

Python client for exact Redis-backed bounded consumption.

Project description

EscrowMint logo

EscrowMint Python

Exact, Redis-backed bounded consumption for shared quotas.

CI GitHub Release License Python Coverage

EscrowMint Python is for cases where many threads, processes, or services need to consume from the same global quota without letting it go below zero.

Good fits:

  • prepaid credits
  • inventory reservation
  • budget caps
  • worker permit pools
  • campaign spend controls

Why EscrowMint

EscrowMint is not a generic counter library. It is a quota and reservation library with application-level semantics:

  • consistent pool top-up
  • exact bounded decrement
  • idempotent consume
  • reservation with TTL
  • commit and cancel flow
  • crash recovery via lazy expiry reclaim

Install

Install from PyPI:

uv add escrowmint

or:

pip install escrowmint

Quickstart

from escrowmint import Client

client = Client.from_url("redis://localhost:6379/0")

result = client.try_consume(
    "wallet:123",
    5,
    idempotency_key="req-001",
)

print(result.applied)      # True
print(result.remaining)    # remaining global quota

Top Up Pool

result = client.top_up(
    "wallet:123",
    25,
    idempotency_key="credit-001",
)

print(result.added)        # 25
print(result.available)    # current global quota after top-up

Crash-Safe Reservation

from escrowmint import Client, ReservationExpired

client = Client.from_url("redis://localhost:6379/0")

reservation = client.reserve(
    "wallet:123",
    10,
    ttl_ms=30_000,
)

try:
    result = client.commit("wallet:123", reservation.reservation_id)
except ReservationExpired:
    # the hold expired and the quota was released
    ...

If a worker crashes after reserve but before commit, the held quota is released after TTL expiry on the next mutation or get_state call for that same resource.

Current API

client.try_consume(resource, amount, idempotency_key=None)
client.top_up(resource, amount, idempotency_key=None)
client.reserve(resource, amount, ttl_ms=..., reservation_id=None)
client.commit(resource, reservation_id)
client.cancel(resource, reservation_id)
client.get_state(resource)

Chunk Lease Path

EscrowMint also ships an explicit chunk-lease lifecycle for hot resources:

lease = client.allocate_chunk(
    "wallet:123",
    100,
    owner_id="worker-a",
    ttl_ms=30_000,
)

result = client.consume_chunk("wallet:123", lease.lease_id, 5, owner_id="worker-a")
lease = client.renew_chunk("wallet:123", lease.lease_id, owner_id="worker-a", ttl_ms=30_000)
lease = client.release_chunk("wallet:123", lease.lease_id, owner_id="worker-a")

This is the authoritative distributed chunk path. It keeps chunk state in Redis and supports expiry reclaim, renew, release, and worker ownership checks.

How It Works

  • Redis remains the source of truth for each resource.
  • Lua scripts make each operation atomic.
  • Reservations move units from available to reserved.
  • Top-ups add units back into available without bypassing expiry reclaim.
  • Pending reservations are indexed by expiry time in Redis.
  • Expired reservations are reclaimed lazily in bounded batches on the next touch of that resource.
  • Terminal reservation outcomes are moved into short-lived receipt keys so the hot reservation hash stays small.

Direct Path and Chunk Lease Path

EscrowMint currently ships both models.

The direct path is the shared-resource path:

  • try_consume, reserve, commit, and cancel
  • top_up for consistent replenishment of the shared pool
  • exact bounded updates against the resource's shared state
  • the simplest way to get correctness and crash recovery

The chunk lease path adds a worker-owned lease layer on top of that model:

  • allocate_chunk, consume_chunk, renew_chunk, release_chunk, and get_chunk
  • explicit escrow or chunk allocation per worker
  • better control over hot-resource ownership, refill, expiry, and reclaim
  • more operational complexity than the direct path

Choose the direct path when you want the simplest exact path.

Choose the chunk lease path when a resource benefits from explicit worker-level quota management.

The current chunk lease implementation is an authoritative Redis-backed lease lifecycle. It improves the state model for hot resources, but it does not automatically become a no-Redis local fast path. If you want fewer Redis round trips than the shipped chunk API provides, you can layer an in-process chunk consumer on top of the authoritative lease lifecycle.

See docs/CHUNK_LEASES.md.

Development

EscrowMint Python uses uv.

uv sync --dev
uv run ruff check
uv run pytest
uv build

Notes:

Support

  • Python: 3.8+
  • Redis: intended for modern Redis deployments that support Lua scripting and standard key expiry semantics
  • Stability: the package is approaching its first public 0.1.x release; expect the core API to be much more stable than the surrounding release/docs tooling

Release Process

EscrowMint Python uses Conventional Commits and Release Please for semantic versioning and release notes.

  • fix: -> patch release
  • feat: -> minor release
  • feat!: or BREAKING CHANGE: -> major release

When releasable commits land on main, Release Please opens or updates a release PR. Merging that PR updates CHANGELOG.md, creates the vX.Y.Z tag, and creates the GitHub release notes. The dedicated release workflow then verifies the tagged code and publishes the package to PyPI.

Docs

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

escrowmint-0.4.0.tar.gz (9.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

escrowmint-0.4.0-py3-none-any.whl (11.6 kB view details)

Uploaded Python 3

File details

Details for the file escrowmint-0.4.0.tar.gz.

File metadata

  • Download URL: escrowmint-0.4.0.tar.gz
  • Upload date:
  • Size: 9.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for escrowmint-0.4.0.tar.gz
Algorithm Hash digest
SHA256 855fd90f72a36cc5ee39dbf5088f5859d55e6bcfb724c683588a9f81077f665d
MD5 5ff006d31cd2ed4df0be125b00b1f975
BLAKE2b-256 16cd061ae5cf3bc88d863e06c78a175b7da109962d51918931c587943a53d1ac

See more details on using hashes here.

File details

Details for the file escrowmint-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: escrowmint-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 11.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for escrowmint-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cd5074e61b4a704f80f69af304d38474683ba572fe0cfd775c4c5de216885f1a
MD5 2ad152f418061c1ed576c5a260a229cf
BLAKE2b-256 b149144c1decb27dcde9a16be50ac6832d4d57251a8f66bbd60e16211ed930b7

See more details on using hashes here.

Supported by

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