Regression
Before 20240415, GET /api/v2/flags/{projectKey} returned the full set in a
single atomic response — a race-free enumeration. 20240415 switched it to
offset pagination (limit/offset, default 20) with no race-free
alternative.
LD-API-Version |
GET /flags behaviour |
20191212, 20210729, 20220603 |
Full set in one response, no _links.next — atomic snapshot |
20240415 (current) |
Paginated, limit=20 default, _links.next present — not race-free |
_links.next is not a cursor. Its href is just …&limit=20&offset=20 —
offset pagination re-encoded as a link, so following it is identical to
incrementing offset and inherits the same race. A real cursor would carry an
opaque, position-stable token ("resume after key X"), not a numeric offset.
Quick test: if next.href contains offset=, it's position-based and races.
Offset pagination isn't a consistent enumeration: archiving a flag mid-walk
removes it from the archived:false set, shifting all later offsets down by one,
so a flag present for the entire walk can be skipped (unarchive → duplicate).
The unpaged behaviour now only survives on the deprecated versions above, so
once those are enforced, no supported version offers race-free enumeration at all.
Reproduction
# pre-20240415: one atomic page, no next link
curl -s -H "Authorization: $LD_TOKEN" -H "LD-API-Version: 20220603" \
".../flags/$PROJECT?filter=archived:false" \
| jq '{count:(.items|length), hasNext:(._links|has("next")), total:.totalCount}'
# count == totalCount, hasNext == false
# 20240415: paginated, and next.href is offset-based
curl -s -H "Authorization: $LD_TOKEN" -H "LD-API-Version: 20240415" \
".../flags/$PROJECT?filter=archived:false" \
| jq '{count:(.items|length), next:._links.next.href, total:.totalCount}'
# count == 20, next ends in "&offset=20"
Skip race on 20240415: page through ?filter=archived:false; archive any flag
behind the current offset; subsequent offsets shift down and the flag at the next
page boundary is never returned.
Request
Restore race-free complete enumeration on a supported version. Not asking to
revert to unpaged — cursor/keyset pagination keeps the paging model while
being immune to offset shift. (For dotted keys, an S3-style prefix+delimiter
listing would also give bounded, atomic per-subtree pages.)
Regression
Before
20240415,GET /api/v2/flags/{projectKey}returned the full set in asingle atomic response — a race-free enumeration.
20240415switched it tooffset pagination (
limit/offset, default 20) with no race-freealternative.
LD-API-VersionGET /flagsbehaviour20191212,20210729,20220603_links.next— atomic snapshot20240415(current)limit=20default,_links.nextpresent — not race-freeOffset pagination isn't a consistent enumeration: archiving a flag mid-walk
removes it from the
archived:falseset, shifting all later offsets down by one,so a flag present for the entire walk can be skipped (unarchive → duplicate).
The unpaged behaviour now only survives on the deprecated versions above, so
once those are enforced, no supported version offers race-free enumeration at all.
Reproduction
Skip race on
20240415: page through?filter=archived:false; archive any flagbehind the current offset; subsequent offsets shift down and the flag at the next
page boundary is never returned.
Request
Restore race-free complete enumeration on a supported version. Not asking to
revert to unpaged — cursor/keyset pagination keeps the paging model while
being immune to offset shift. (For dotted keys, an S3-style
prefix+delimiterlisting would also give bounded, atomic per-subtree pages.)