First Steps
The smallest grelmicro app is one pattern and one provider. The pattern carries no backend reference and finds the provider on its own.
Install
pip install "grelmicro[redis]"
See the installation guide for uv, poetry, and the other
backend extras.
Reading the examples
grelmicro is async. A function written async def runs only when something
awaits it, and asyncio.run(main()) starts the first one. Examples that run
on their own end with that call. In a web app the server starts it for you, so
you run fastapi run main.py or uvicorn main:app instead.
A block labelled fragment is a piece of a larger file. Put it inside an
async function of your own, where the names around it are already defined.
Mental model
- Pattern: the object your app calls, such as
Lock("cart")orRateLimiter.sliding_window("api", ...). - Provider: owns a connection, such as
RedisProvider. - Component: wires a backend into the app, such as
CacheorCoordination. A Provider on its own registers one for every kind it serves, so you often name none. - Adapter: the concrete backend implementation. Providers usually hide it.
- Ambient binding:
micro.install(app)lets request and message handlers find the currentGrelmicroapp.
Your first app
Guard a shared resource with a distributed Lock. The provider says where the
lock state lives, so every worker takes the same lock.
import asyncio
from grelmicro import Grelmicro
from grelmicro.coordination import Lock
from grelmicro.providers.redis import RedisProvider
redis = RedisProvider("redis://localhost:6379/0")
micro = Grelmicro(uses=[redis])
lock = Lock("cart")
async def checkout() -> None:
async with lock:
print("Protected resource accessed")
async def main() -> None:
# The lock resolves its backend inside the app scope.
async with micro:
await checkout()
if __name__ == "__main__":
asyncio.run(main())
Save it as main.py, start Redis and run it:
docker run -d -p 6379:6379 redis
python main.py
It prints Protected resource accessed. Three things happen here:
Lock("cart")builds a lock namedcartwith default settings.RedisProvider(...)says where the shared state lives.Grelmicro(uses=[...])wires it into the app.
The lock carries no backend reference. It finds one when it is used, inside
async with micro:, which is why checkout() is called from there. In a web
app micro.install(app) extends that scope to your request handlers, so
handlers need no async with.
One caller holds cart at a time. The next caller waits for the release.
Construct a pattern
Every pattern is built the same way. Pass the name first, then tune with keyword arguments:
from grelmicro.coordination import Lock
lock = Lock("cart", lease_duration=60)
Patterns with variants use factory methods:
from grelmicro.resilience import RateLimiter
api = RateLimiter.sliding_window("api", limit=100, window=60)
Decorators take the same keyword arguments:
from grelmicro.cache import cached
@cached(ttl=30)
async def get_user(user_id: int) -> User:
...
Next
You built a pattern and wired it into an app. Next, wire it into a web
framework with micro.install(app).