Skip to content

v0.4.0: bug fixes, batch ops, scoring, streaming, serializers and retention - #5

Merged
jkyberneees merged 3 commits into
mainfrom
claude/fix-bugs-top5-features
Oct 24, 2026
Merged

jkyberneees merged 3 commits into
mainfrom
claude/fix-bugs-top5-features

Conversation

@jkyberneees

Copy link
Copy Markdown
Contributor

Minor release 0.4.0. It fixes functional bugs in both repositories and adds five features. Three independent reviews (Redis internals, API/developer experience, reliability/testing) shaped the plan.

Bug fixes

ItemRepository

  • Keys are now listed with SCAN instead of KEYS, which blocked the server. On Redis Cluster the scan runs on every master; before, it only reached one node.
  • Reads and deletes no longer send multi-key MGET/DEL on Cluster, which failed with CROSSSLOT errors.
  • Repository names are matched literally. Before, a name like * acted as a glob, so deleteAll() could delete other repositories' data.
  • It no longer picks up the index key of a SortedItemRepository with the same name. That key showed up as a null entry, made count one too high, and deleteAll() deleted it.
  • Pagination order is stable across calls.

SortedItemRepository

  • Writes and deletes that touch both the index and the data key run as one MULTI transaction. On Cluster without a hash tag they run in a safe order instead: data before index on writes, index before data on deletes.
  • set no longer makes the extra hexists/zrem round trips.
  • Scores are now microseconds since the epoch and strictly increase within a process. They used to come from process.hrtime, which can't be compared across processes or restarts and loses precision after about 104 days of uptime.

Both

  • An out-of-range page now reports the real total count instead of 0.
  • Methods that return lists drop entries whose data is missing, so they no longer contain hidden nulls.
  • page, pageSize, n, TTL and id are validated and throw a RangeError or TypeError when invalid. Before, pageSize = 0 or n = 0 selected the whole repository, and with deletePage deleted it.
  • The build script no longer fails when dist/ doesn't exist.

Features

  1. Batch operations: setMany, getByIds (results keep the input order, null for missing ids) and deleteByIds.
  2. Sorted scoring and ordering:
    • set(item, { score, keepScore })
    • getPaginated(page, size, 'desc')
    • getItemsByScore(min, max, { offset, limit, order }), which also accepts '-inf'/'+inf' bounds
  3. Streaming: iterate({ batchSize, order }) async iterators, so you can walk a repository without loading all of it.
  4. Constructor options:
    • a pluggable serializer (MessagePack stays the default)
    • hashTag for atomic transactions on Redis Cluster
  5. TTL and retention: getTtl, expire and persist on ItemRepository; deleteByScore, trimToSize and a maxItems cap on SortedItemRepository.

Behavior changes

  • IItemRepository and ISortedItemRepository gain required methods, so custom implementations of these interfaces must add them.
  • Sorted items now get wall-clock scores. Items stored by earlier versions with hrtime scores may sort out of order relative to new ones.
  • Invalid pagination, n, TTL or empty-id arguments now throw instead of doing something surprising.
  • Unchanged on purpose: updating a sorted item still moves it to the end by default. Pass keepScore: true to keep its position.

Known limitations

  • Repository a can still see keys of repository a:b: a key like items:a:b:1 could belong to either. Fixing this needs a new key layout.
  • deletePage, deleteByScore and trimToSize read the matching ids, then delete them, so they can race with concurrent writers.

Testing

  • bun run test: 51 pass, including 27 new tests. Of the new tests, 25 fail on the previous code. The other two (concurrent set/delete consistency, ordering across repository instances) pass on both versions, because those problems don't reproduce inside a single test process.
  • Hardened the existing tests: assertions that passed on empty results now check counts, and repository names use crypto.randomUUID().
  • tsc --noEmit, bun run lint and bun run build are clean.
  • Checked both repositories by hand against a local 3-node Redis Cluster, with and without hashTag.

Generated by Claude Code

… and retention

Bug fixes:
- ItemRepository: replace blocking KEYS with SCAN (fanned out to all masters on
  Redis Cluster), avoid CROSSSLOT MGET/DEL on Cluster, match repository names
  literally (glob escaping), stop colliding with the index key of a
  SortedItemRepository with the same name, stable ordered pagination
- SortedItemRepository: atomic MULTI writes/deletes (ordered, safe writes on
  Cluster), drop redundant hexists/zrem round trips, wall-clock monotonic scores
  instead of process.hrtime (meaningless across processes, lost precision)
- Both: real total count on out-of-range pages, read methods no longer return
  hidden nulls, validate page/pageSize/n/TTL/id instead of silently selecting
  (or deleting) the whole repository
- build script no longer fails without an existing dist/

Features:
- Batch operations: setMany, getByIds, deleteByIds
- Sorted repository scoring and ordering: explicit score, keepScore, desc
  pagination, infinite bounds plus offset/limit/order on getItemsByScore
- Streaming via iterate() async iterators
- Constructor options: pluggable serializer, Cluster hashTag for atomic
  transactions on Redis Cluster
- TTL and retention: getTtl/expire/persist, deleteByScore, trimToSize, maxItems

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLbcuU2UWtjZCuWZa1QTkE
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLbcuU2UWtjZCuWZa1QTkE
package-lock.json was generated on darwin-arm64 and only records that
platform's @rometools/cli package, so the bun install in CI skipped the
linux-x64 binary and `bun run lint` failed with "Cannot find module
'@rometools/cli-linux-x64/rome'". bun.lock takes precedence for bun and
records every platform's binary; all dependency versions match
package-lock.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLbcuU2UWtjZCuWZa1QTkE
@jkyberneees
jkyberneees merged commit eab1bc2 into main Oct 24, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants