You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by "steveahnahn (via GitHub)" <gi...@apache.org> on 2026/06/22 19:37:49 UTC
[I] Cursor pagination silently drops rows when sorting by a nullable column [airflow]
steveahnahn opened a new issue, #68858:
URL: https://github.com/apache/airflow/issues/68858
### Under which category would you file this issue?
Airflow Core
### Apache Airflow version
`main` (and any released 3.x line that ships cursor pagination; affected code in `airflow/api_fastapi/common/cursors.py`)
### What happened and how to reproduce it?
The public REST list endpoints `GET .../dagRuns` and `GET .../taskInstances`, when paginated with `cursor` and sorted by a **nullable** column (`start_date`, `end_date`, `duration`, `run_after`), **silently drop rows** — no error. When the column has `NULL`s and the result spans more than one page, every row on one side of the NULL/non-NULL boundary is never returned.
**All backends are affected** — which side vanishes flips with NULL ordering: SQLite/MySQL `ASC` (and Postgres `DESC`) drop the **non-NULL** rows; the reverse drops the **NULL** rows.
Reproduction (self-contained, uses the real pagination helpers):
```python
from sqlalchemy import create_engine, Integer, String, Column, select
from sqlalchemy.orm import declarative_base, Session
from airflow.api_fastapi.common.parameters import SortParam
from airflow.api_fastapi.common.cursors import encode_cursor, apply_cursor_filter
Base = declarative_base()
class M(Base):
__tablename__ = "m"
id = Column(Integer, primary_key=True)
val = Column(String, nullable=True) # nullable sort column, e.g. start_date
eng = create_engine("sqlite://")
Base.metadata.create_all(eng)
with Session(eng) as s:
s.add_all([M(id=i, val=v) for i, v in [(1, None), (2, None), (3, None), (4, "a"), (5, "b"), (6, "c")]])
s.commit()
sort = SortParam(allowed_attrs=["val"], model=M); sort.set_value(["val"]) # ORDER BY val ASC
returned, token = [], None
with Session(eng) as s:
while True:
stmt = sort.to_orm(select(M)).limit(2)
if token is not None:
stmt = apply_cursor_filter(stmt, token, sort)
rows = s.scalars(stmt).all()
if not rows:
break
returned.extend(m.id for m in rows)
token = encode_cursor(rows[-1], sort)
print(returned) # [1, 2, 3] -> rows 4, 5, 6 silently dropped
```
`ASC` returns `[1, 2, 3]` (rows 4,5,6 missing); `DESC` drops `[1, 2, 3]`.
**Root cause** (`airflow/api_fastapi/common/cursors.py`): for a `NULL` cursor value, `_nested_keyset_predicate` reduces to `col IS NULL AND pk > pk_value` — the `col IS NOT NULL` branch meant to cross the boundary is dead. Non-NULL comparisons are also `UNKNOWN` for NULL rows, so pagination never crosses the boundary from either side. And `SortParam.to_orm` emits a plain `ASC`/`DESC` (no `NULLS FIRST/LAST`), so NULL position is the dialect default the predicate can't match.
### What you think should happen instead?
Every matching row returned exactly once, regardless of NULLs or backend. A fix needs the `ORDER BY` and the keyset predicate to agree on a deterministic NULL placement — e.g. an explicit null-rank sort key also encoded in the cursor — which would make NULL ordering consistent across dialects (a behavior change on some dialects, worth discussing).
### Operating System
Any — backend-dependent, not OS-dependent.
### Deployment
Other (local reproduction; not deployment-specific).
### Versions of Apache Airflow Providers
Not applicable (airflow-core).
### Official Helm Chart version
Not Applicable
### Anything else?
Happens every time the conditions hold; silent, so clients (UIs, scripts, exports) can't tell rows are missing.
### Are you willing to submit PR?
- [ ] Yes I am willing to submit a PR!
### Code of Conduct
- [x] I agree to follow this project's Code of Conduct
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [I] Cursor pagination silently drops rows when sorting by a nullable column [airflow]
Posted by "pierrejeambrun (via GitHub)" <gi...@apache.org>.
pierrejeambrun closed issue #68858: Cursor pagination silently drops rows when sorting by a nullable column
URL: https://github.com/apache/airflow/issues/68858
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@airflow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org