Release Notes¶
This page is the source of truth for aio-fluid release notes. Each section
below maps to a tagged release on
GitHub. When a new tag is
pushed, the matching section is extracted by
.github/workflows/release.yml and published as the GitHub Release body.
v2.9.1¶
A fix for database filters with long lists of values.
Improvements and fixes¶
- A list passed as a filter value is sent to the database as a single array
parameter (
= ANYforeq,<> ALLforne) rather than one parameter per value, so it can hold any number of values without hitting the 32767 parameter limit of a query. An empty list matches no row witheqand excludes none withne(database tutorial, #121).
v2.9.0¶
Database queries can select only the columns they need, and pooled connections survive a database restart.
New features¶
CrudDB.db_selectandPagination.executeaccept acolumnsargument to select only some columns rather than every column of the table. Large columns a query does not use are no longer read, and computed expressions can be selected alongside table columns (database tutorial, #120).
Improvements and fixes¶
- Pooled database connections are tested when taken from the pool, so the
connections a server dropped, for instance on a database restart or
failover, are replaced rather than failing the next query. It is on by
default and set by the new
FLUID_DBPOOL_PRE_PINGsetting (settings, #120).
v2.8.3¶
A small addition to the task information served by the task manager API.
Improvements and fixes¶
- Task information returned by the
/tasksendpoint now includescpu_boundandmax_concurrency, so clients can see how each task executes without inspecting its code (#119).
v2.8.2¶
A fix for async task event handlers, the task database plugin included, silently stopping in a running consumer.
Improvements and fixes¶
- The task database plugin
no longer holds a Redis lock around its writes. The lock expired when a write
took longer than five seconds, and releasing it raised
LockNotOwnedError. SinceCrudDB.db_upsertis atomic (v2.8.0) the lock is not needed (#118). - A task consumer stops when its async dispatcher stops running. An exception raised by an async handler ended the dispatcher while the consumer kept executing tasks, so every later event was silently dropped. The consumer now shuts down instead, and the application restarts (#118).
v2.8.1¶
A fix for CPU bound tasks run with the exec command of the task manager CLI.
Improvements and fixes¶
- Running a CPU bound task with
execoutside a consumer no longer spawns a further subprocess: the command calls the function the task was declared with directly, in its own process. A CPU bound task keeps that function wherever it is loaded, so the same task object works both in the consumer and in the subprocess it spawns (#117). - TaskManager.execute
accepts
in_process=Trueto run a CPU bound task in the current process instead of spawning one for it (#117).
v2.8.0¶
Upserts are atomic and can write many rows at once. CrudDB.db_upsert now
runs a single INSERT ... ON CONFLICT (...) DO UPDATE statement, which
requires its lookup columns to match a unique constraint: see Breaking
changes below.
Breaking changes¶
- CrudDB.db_upsert
requires the columns of
filtersto match exactly a primary key or unique constraint of the table. Upserting on columns without one now fails withthere is no unique or exclusion constraint matching the ON CONFLICT specification. Filter operators such asscore:gtare no longer accepted and raiseValueError. - The
filtersanddatapassed todb_upsertmust together form a valid row to insert, even when the row already exists, since Postgres checksNOT NULLconstraints before it detects the conflict. db_upsertwithoutdataupdates the existing row to its own key values instead of selecting it, so it takes a row lock and firesUPDATEtriggers.- Column
onupdatedefaults are not applied whendb_upsertupdates an existing row.
New features¶
- CrudDB.db_upsert_many
upserts a list of records on the given key columns, in batches of
batch_sizerecords (1000 by default) within a single transaction. Every record must have the same columns and no two records can share a key. See the upsert tutorial.
v2.7.0¶
An application can now place its tables on a database schema other than
public. The database layer creates the schema for create_all, lists
non-system schemas, and drop_all_schemas drops an explicit set of schemas.
New features¶
- The Database metadata can hold
tables on a schema other than
publicby passingschemato the table or aMetaData(schema=...)toCrudDB.from_env.Migration.create_allcreates the declared schemas before the tables, so the tables do not fail withInvalidSchemaName. Migration.schemas()returns the non-system schemas in the database.Migration.drop_all_schemas(schemas=None)accepts an explicit sequence of schemas to drop; when omitted it keeps dropping onlypublic, as before.
v2.6.0¶
An application that aliases the task run to bind its dependencies keeps the parameters of its tasks. Python 3.11 is no longer supported: see Breaking changes below.
Breaking changes¶
- Python 3.11 is no longer supported, the minimum version is now 3.12. The
alias resolution below relies on
typing.TypeAliasType, which the standard library only exposes from 3.12.
Improvements and fixes¶
- A task annotated with a type alias of the task run keeps its parameters
model. An application binding its dependencies once, as in
type AppTaskRun[P] = TaskRun[P, AppDeps], and annotating a task withAppTaskRun[Params]used to silently get EmptyParams: an alias is not a class, so it did not survive the inspection of the annotation, and the parameters passed when queueing the task were dropped. The alias is now resolved to the type it stands for before the annotation is read, both bare and subscripted.
v2.5.0¶
Task runs can queue other task runs and the chain is recorded on every run, CPU bound tasks behave the same way in a subprocess as on the event loop, and Kubernetes Jobs inherit the task timeout. The documentation gained a settings reference, a recipes cheat sheet and a page on pointing coding agents at the library.
- A task run can queue another task run with
TaskRun.queue, which
records the queueing run in
from_run_idand carries theroot_run_idof the first run in the chain, so any run can be traced back to the one that started it. (#110) - CPU bound tasks now start the async event dispatcher in the subprocess that
runs them, so lifecycle events reach the handlers and plugins there as well,
and a consumer with CPU bound tasks that is not started from
TaskManagerCLI raises the
new
CpuBoundEntryPointErroron startup rather than failing when the first such task runs. - A Kubernetes Job created for a CPU bound task sets
active_deadline_secondsfrom the tasktimeout_seconds, so the cluster terminates a Job that overruns. (#108) - New Settings reference page covering the environment variables that configure the task consumer, broker, database and HTTP client, including the prefix rules and the fields that keep an unprefixed name.
- Fixed the Task Broker tutorial, which imported a name that does not exist, quoted the wrong default Redis port, and listed six outdated abstract methods instead of the sixteen a broker has to implement.
- Admonition blocks are rendered as admonitions instead of literal text, which also fixes the note on retry delays in the Task Retry reference.
- New recipes cheat sheet, a page on
using the docs with AI agents and
an
AGENTS.mdfor contributors. (#111) - New tutorials on task dependencies, choosing a task manager and extending the FastAPI app. (#109, #111)
- New comparison page placing the library next to Celery, RQ, arq and taskiq, with download numbers refreshed by a scheduled workflow, and a landing page rewritten around CPU bound work. (#104, #105, #107)
v2.4.3¶
Fixes two task queue issues: pydantic secret params were masked when a task run was serialized to the queue, and a params validation error on the consumer side crashed the worker.
- Secret params now survive the round-trip through the task queue and the
cpu-bound subprocess. Task runs are serialized for the queue with secret
values revealed via the new
params_dumphelper; all other dumps (logs, endpoints) keep secrets masked. (#103) - A task run consumed from the queue with invalid params no longer kills the
consumer worker. The broker raises the new
TaskParamsErrorcarrying the task run, and the consumer logs the error and marks the run as failed. (#103)
v2.4.2¶
Tasks can now be tagged at registration time.
- The task registration methods accept an optional
tagsargument. When provided, the extra tags are merged into each task's own tags as it is registered — applied acrossregister_task,register_from_module, andregister_from_dict. (#102) - Bumped
python-json-loggerto>= 4.1.0and switched JSON logging to the newpythonjsonlogger.jsonformatter path (the oldpythonjsonlogger.jsonloggermodule is deprecated). (#102)
v2.4.1¶
Task history can now be filtered by task tags.
- Added a
tagsfield to task history queries. Runs match when their task carries at least one of the given tags, resolved against the live registry. (#101)
v2.4.0¶
Lazy settings via pydantic-settings, JSONB params filtering for task history, and customisable route prefixes.
- Settings are now lazy — resolved on first access instead of at import
time. Env vars use a
FLUID_prefix by default; legacy unprefixed names are kept as aliases. (#100) - The task database plugin accepts a
route_prefixparameter for customising history route URLs and replaceswith_task_history_router()with aregister_routes()method. (#100) - Task history queries support filtering by run params via a new
paramsfield (renamed fromHistoryQuerytoTaskHistoryQuery). (#99) - Database migration required: the
paramscolumn is nowJSONBwith a GIN index. See the example migration for the schema changes. - Removed
get_loggerfromfluid.utils.log. Task loggers are now obtained directly vialogging.getLogger(module).
v2.3.1¶
v2.3.0 is broken — do not use it.
Fixes a regression in v2.3.0 where the httpx2 dependency was pinned to
>=2.2.0, which fails on Python 3.14 builds missing the _zstd C extension.
Pins httpx2 to >=2.0.0, <2.1.0 and switches all httpx imports to
httpx2 for correct namespace resolution.
httpx2is now pinned to>=2.0.0, <2.1.0— versions 2.1.0+ require thecompression.zstdstdlib module which is not available in all Python 3.14 builds. (#98)- All
import httpxstatements replaced withimport httpx2 as httpx(orfrom httpx2 import ...) to ensure correct namespace resolution regardless ofhttpx2version. - Added test coverage for HttpxClient and HttpxResponse.
v2.3.0¶
Moves development and documentation dependencies from optional-dependencies to
dependency groups, switches to
httpx2 for HTTP client support, and removes
the inflection dependency.
devanddocsdependencies are now declared under[dependency-groups]instead of[project.optional-dependencies]. Installed viauv sync --all-groups.- The
httpextra now useshttpx2instead ofhttpx.httpx2provides the samehttpxmodule so no code changes are required. (#97) - The
inflectiondependency has been removed. (#96)
v2.2.6¶
Adds tag filtering for task listings and fixes a race in the task database plugin.
- Task listings can be filtered by tag: the
GET /tasksendpoint and thelscommand of the task CLI accept a repeatabletagsoption that returns only tasks carrying at least one of the given tags, andTaskInfonow reports each task's tags. (#94) - The task database plugin
now serialises its per-run lifecycle writes with a dedicated task-run lock.
CrudDB.db_upsertis not atomic — it issues anUPDATEand onlyINSERTs when nothing matched — so when the scheduler wrote thequeuedrow and a consumer wrote therunningrow a few milliseconds later, the consumer'sUPDATEcould miss the not-yet-committedINSERT, fall through to its ownINSERTand violate the task-runs primary key. Holding the lock around the upsert removes the race. (#95) - TaskRun.lock
accepts an optional
nameto acquire a named sub-lock for the task run, andtimeoutnow defaults toNone. (#95)
v2.2.5¶
- The task decorator
accepts an
envmapping of extra environment variables, injected into the subprocess for CPU-bound tasks and forwarded to the container for tasks dispatched as Kubernetes Jobs. (#90)
v2.2.4¶
Bug-fix release for the task scheduler.