Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changie.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ versionExt: md
versionFormat: '## [{{ .Version }}](https://github.com/skyoo2003/kvs/releases/tag/{{ .Version }}) - {{ .Time.Format "2006-01-02" }}'
kindFormat: "### {{ .Kind }}"
changeFormat: "* {{ .Body }} ([#{{ .Custom.Issue }}](https://github.com/skyoo2003/kvs/issues/{{ .Custom.Issue }}))"
body:
# One sentence, two at most. 280 characters is about where the second one
# ends; a fragment that needs more is carrying documentation, which belongs
# on a page the notes can link to.
minLength: 10
maxLength: 280
kinds:
- label: Added
- label: Changed
Expand Down
22 changes: 11 additions & 11 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
## [v1.0.0](https://github.com/skyoo2003/kvs/releases/tag/v1.0.0) - 2026-08-12
### Added
* Redis/Valkey compatible RESP2 server on 127.0.0.1:6379, sharing one keyspace with the HTTP and gRPC APIs; it steps aside if the port is taken, holds at most 10000 connections, and bounds one transaction's queue at 64MiB so a single client cannot exhaust memory ([#232](https://github.com/skyoo2003/kvs/issues/232))
* Lua scripting with EVAL, EVALSHA, and SCRIPT LOAD/EXISTS/FLUSH; a script runs sandboxed under one write lock, so its redis.call sequence is atomic, and gives up after 5 seconds rather than hold the store ([#232](https://github.com/skyoo2003/kvs/issues/232))
* A Redis/Valkey compatible RESP2 server on 127.0.0.1:6379, sharing one keyspace with the HTTP and gRPC APIs. It steps aside if the port is taken, holds at most 10000 connections, and bounds one transaction's queue at 64MiB ([#232](https://github.com/skyoo2003/kvs/issues/232))
* Lua scripting with EVAL, EVALSHA, and SCRIPT LOAD/EXISTS/FLUSH, sandboxed under one write lock so a script's redis.call sequence is atomic. A script gives up after 5 seconds rather than hold the store ([#232](https://github.com/skyoo2003/kvs/issues/232))
* Keep the keyspace across restarts with `kvs serve --data-dir`, which appends every change to a log and replays it at startup ([#236](https://github.com/skyoo2003/kvs/issues/236))
* The cjson library inside a script, so cjson.encode and cjson.decode work under EVAL instead of failing on a nil global; a JSON null decodes to cjson.null rather than nil, so it does not end the array it sits in ([#232](https://github.com/skyoo2003/kvs/issues/232))
* The cjson library inside a script, so cjson.encode and cjson.decode work under EVAL instead of failing on a nil global. A JSON null decodes to cjson.null rather than nil, so it does not end the array it sits in ([#232](https://github.com/skyoo2003/kvs/issues/232))
* Run a Raft cluster with `kvs serve --raft-addr` and `--join`; writes pass consensus, and losing the leader triggers an election instead of needing a person ([#236](https://github.com/skyoo2003/kvs/issues/236))
* A compatibility page states what v1 promises not to break, what it leaves out, and the trust boundary kvs assumes; the exported Go surface is pinned in testdata/api-surface.txt and checked by a test, so it cannot widen or narrow unnoticed ([#244](https://github.com/skyoo2003/kvs/issues/244))
* A data directory now carries a format version, and kvs refuses to start on one it does not recognise instead of replaying bytes another version laid out; the refusal names both versions and what to do about it, and it covers the Raft store as well as the append log ([#245](https://github.com/skyoo2003/kvs/issues/245))
* A compatibility page stating what v1 promises not to break, what it leaves out, and the trust boundary kvs assumes. The exported Go surface is pinned in testdata/api-surface.txt and checked by a test, so it cannot widen or narrow unnoticed ([#244](https://github.com/skyoo2003/kvs/issues/244))
* A data directory now carries a format version, and kvs refuses to start on one it does not recognise, naming both versions and what to do about it. This covers the Raft store as well as the append log ([#245](https://github.com/skyoo2003/kvs/issues/245))
### Changed
* Container images now run 'serve' by default as UID 65534 and no longer declare a volume ([#228](https://github.com/skyoo2003/kvs/issues/228))
* RESP SCAN, HSCAN, SSCAN, and ZSCAN now page their walk, WATCH tracks only the keys it was given, lists cost O(1) at both ends, and expired keys are reclaimed by a sampling sweep ([#232](https://github.com/skyoo2003/kvs/issues/232))
* INFO and HELLO now report what a clustered node actually is — a follower answers role:slave and names the leader in master_host and master_port, a leader counts the others in connected_slaves, and cluster_enabled:0 says kvs is not Redis Cluster ([#236](https://github.com/skyoo2003/kvs/issues/236))
* Homebrew installs a cask instead of a formula; `brew install skyoo2003/tap/kvs` is unchanged, and the tap carries a tap_migrations.json entry so an existing formula install moves across on upgrade. GoReleaser deprecated the formula path for pre-built binaries, and the release would have broken on a floating version of it ([#248](https://github.com/skyoo2003/kvs/issues/248))
* INFO and HELLO now report what a clustered node actually is: a follower answers role:slave and names the leader, a leader counts the others in connected_slaves, and cluster_enabled:0 says kvs is not Redis Cluster ([#236](https://github.com/skyoo2003/kvs/issues/236))
* Homebrew installs a cask instead of a formula, since GoReleaser deprecated the formula path for pre-built binaries. `brew install skyoo2003/tap/kvs` is unchanged, and a tap_migrations.json entry moves an existing install across ([#248](https://github.com/skyoo2003/kvs/issues/248))
### Removed
* Drop the unused data structure packages pkg/bitset, pkg/cuckoofilter, pkg/lsm, and pkg/rbt; the storage engine went to an append log and Raft instead, and nothing in the server or the library imported them. pkg/resp stays, since the RESP server is built on it ([#236](https://github.com/skyoo2003/kvs/issues/236))
* Drop the unused data structure packages pkg/bitset, pkg/cuckoofilter, pkg/lsm, and pkg/rbt, which nothing imported once the storage engine went to an append log and Raft. pkg/resp stays, since the RESP server is built on it ([#236](https://github.com/skyoo2003/kvs/issues/236))
### Fixed
* Fix multi-arch (linux/arm64) release images and expose the gRPC port 3457 in container images ([#228](https://github.com/skyoo2003/kvs/issues/228))
* An HTTP 405 now carries the same JSON error body as every other HTTP error, instead of an empty response the documented contract did not allow ([#244](https://github.com/skyoo2003/kvs/issues/244))
### Documentation
* The durability and clustering page now carries measured numbers from a four hour run under load with a node stopped every thirty seconds and kept down for ten - 329,631 acknowledged writes, 111,516 of them taken while a node was gone, and none of them lost across 479 restarts - along with 51 bytes of append log per write, the Raft log a node too busy restarting never truncates, and a make soak target that reproduces all of it ([#246](https://github.com/skyoo2003/kvs/issues/246))
* The release page now covers what one tag actually produces, the checks worth running before pushing it, and an upgrade section measured by running v0.1.1 and v1 side by side - the library, the CLI, and gRPC unchanged, an HTTP 405 now carrying a JSON body, a RESP listener appearing on loopback, and no data directory to migrate because v0.1.1 never wrote one ([#248](https://github.com/skyoo2003/kvs/issues/248))
* The clustering page no longer claims that a node restarting without pause grows the heap. Reproducing the original condition for four hours - the same 479 restarts, each node back the instant it stopped - moved the heap from 6.7MB to 7.0MB where the earlier figure implies about 130MB; that figure came from the harness before its load and its fault injection were separated. The Raft log still grows, and instant restarts reached 202MB a node against 126MB, because it grows with writes. `SOAK_DOWN=0` reproduces the condition and the cluster soak now fails if the heap more than doubles ([#248](https://github.com/skyoo2003/kvs/issues/248))
* The durability and clustering page now carries numbers from a four hour run with a node stopped every thirty seconds: 329,631 acknowledged writes, none lost across 479 restarts, and 51 bytes of append log per write. `make soak` reproduces it ([#246](https://github.com/skyoo2003/kvs/issues/246))
* The release page now covers what one tag actually produces, the checks worth running before pushing it, and an upgrade section measured by running v0.1.1 and v1 side by side ([#248](https://github.com/skyoo2003/kvs/issues/248))
* The clustering page no longer claims that a node restarting without pause grows the heap; repeating the run moved it from 6.7MB to 7.0MB where the old figure implies about 130MB. The Raft log still grows with writes, and the soak now fails on a doubled heap ([#248](https://github.com/skyoo2003/kvs/issues/248))
### Misc
* Migrate golangci-lint config to v2 and pin the linter to v2.12.2 in CI ([#229](https://github.com/skyoo2003/kvs/issues/229))
## [v0.1.1](https://github.com/skyoo2003/kvs/releases/tag/v0.1.1) - 2026-04-20
Expand Down
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ make setup # installs pre-commit hooks

- Keep PRs small and focused on a single concern
- Include tests for new functionality
- Add a changelog fragment for user-visible changes: `changie new`, one sentence, two at most (see
[Release Process](https://skyoo2003.github.io/kvs/docs/release/))
- Ensure `make all` passes (lint + test + build)
- Update documentation if behavior changes

Expand Down
22 changes: 11 additions & 11 deletions changes/v1.0.0.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
## [v1.0.0](https://github.com/skyoo2003/kvs/releases/tag/v1.0.0) - 2026-08-12
### Added
* Redis/Valkey compatible RESP2 server on 127.0.0.1:6379, sharing one keyspace with the HTTP and gRPC APIs; it steps aside if the port is taken, holds at most 10000 connections, and bounds one transaction's queue at 64MiB so a single client cannot exhaust memory ([#232](https://github.com/skyoo2003/kvs/issues/232))
* Lua scripting with EVAL, EVALSHA, and SCRIPT LOAD/EXISTS/FLUSH; a script runs sandboxed under one write lock, so its redis.call sequence is atomic, and gives up after 5 seconds rather than hold the store ([#232](https://github.com/skyoo2003/kvs/issues/232))
* A Redis/Valkey compatible RESP2 server on 127.0.0.1:6379, sharing one keyspace with the HTTP and gRPC APIs. It steps aside if the port is taken, holds at most 10000 connections, and bounds one transaction's queue at 64MiB ([#232](https://github.com/skyoo2003/kvs/issues/232))
* Lua scripting with EVAL, EVALSHA, and SCRIPT LOAD/EXISTS/FLUSH, sandboxed under one write lock so a script's redis.call sequence is atomic. A script gives up after 5 seconds rather than hold the store ([#232](https://github.com/skyoo2003/kvs/issues/232))
* Keep the keyspace across restarts with `kvs serve --data-dir`, which appends every change to a log and replays it at startup ([#236](https://github.com/skyoo2003/kvs/issues/236))
* The cjson library inside a script, so cjson.encode and cjson.decode work under EVAL instead of failing on a nil global; a JSON null decodes to cjson.null rather than nil, so it does not end the array it sits in ([#232](https://github.com/skyoo2003/kvs/issues/232))
* The cjson library inside a script, so cjson.encode and cjson.decode work under EVAL instead of failing on a nil global. A JSON null decodes to cjson.null rather than nil, so it does not end the array it sits in ([#232](https://github.com/skyoo2003/kvs/issues/232))
* Run a Raft cluster with `kvs serve --raft-addr` and `--join`; writes pass consensus, and losing the leader triggers an election instead of needing a person ([#236](https://github.com/skyoo2003/kvs/issues/236))
* A compatibility page states what v1 promises not to break, what it leaves out, and the trust boundary kvs assumes; the exported Go surface is pinned in testdata/api-surface.txt and checked by a test, so it cannot widen or narrow unnoticed ([#244](https://github.com/skyoo2003/kvs/issues/244))
* A data directory now carries a format version, and kvs refuses to start on one it does not recognise instead of replaying bytes another version laid out; the refusal names both versions and what to do about it, and it covers the Raft store as well as the append log ([#245](https://github.com/skyoo2003/kvs/issues/245))
* A compatibility page stating what v1 promises not to break, what it leaves out, and the trust boundary kvs assumes. The exported Go surface is pinned in testdata/api-surface.txt and checked by a test, so it cannot widen or narrow unnoticed ([#244](https://github.com/skyoo2003/kvs/issues/244))
* A data directory now carries a format version, and kvs refuses to start on one it does not recognise, naming both versions and what to do about it. This covers the Raft store as well as the append log ([#245](https://github.com/skyoo2003/kvs/issues/245))
### Changed
* Container images now run 'serve' by default as UID 65534 and no longer declare a volume ([#228](https://github.com/skyoo2003/kvs/issues/228))
* RESP SCAN, HSCAN, SSCAN, and ZSCAN now page their walk, WATCH tracks only the keys it was given, lists cost O(1) at both ends, and expired keys are reclaimed by a sampling sweep ([#232](https://github.com/skyoo2003/kvs/issues/232))
* INFO and HELLO now report what a clustered node actually is — a follower answers role:slave and names the leader in master_host and master_port, a leader counts the others in connected_slaves, and cluster_enabled:0 says kvs is not Redis Cluster ([#236](https://github.com/skyoo2003/kvs/issues/236))
* Homebrew installs a cask instead of a formula; `brew install skyoo2003/tap/kvs` is unchanged, and the tap carries a tap_migrations.json entry so an existing formula install moves across on upgrade. GoReleaser deprecated the formula path for pre-built binaries, and the release would have broken on a floating version of it ([#248](https://github.com/skyoo2003/kvs/issues/248))
* INFO and HELLO now report what a clustered node actually is: a follower answers role:slave and names the leader, a leader counts the others in connected_slaves, and cluster_enabled:0 says kvs is not Redis Cluster ([#236](https://github.com/skyoo2003/kvs/issues/236))
* Homebrew installs a cask instead of a formula, since GoReleaser deprecated the formula path for pre-built binaries. `brew install skyoo2003/tap/kvs` is unchanged, and a tap_migrations.json entry moves an existing install across ([#248](https://github.com/skyoo2003/kvs/issues/248))
### Removed
* Drop the unused data structure packages pkg/bitset, pkg/cuckoofilter, pkg/lsm, and pkg/rbt; the storage engine went to an append log and Raft instead, and nothing in the server or the library imported them. pkg/resp stays, since the RESP server is built on it ([#236](https://github.com/skyoo2003/kvs/issues/236))
* Drop the unused data structure packages pkg/bitset, pkg/cuckoofilter, pkg/lsm, and pkg/rbt, which nothing imported once the storage engine went to an append log and Raft. pkg/resp stays, since the RESP server is built on it ([#236](https://github.com/skyoo2003/kvs/issues/236))
### Fixed
* Fix multi-arch (linux/arm64) release images and expose the gRPC port 3457 in container images ([#228](https://github.com/skyoo2003/kvs/issues/228))
* An HTTP 405 now carries the same JSON error body as every other HTTP error, instead of an empty response the documented contract did not allow ([#244](https://github.com/skyoo2003/kvs/issues/244))
### Documentation
* The durability and clustering page now carries measured numbers from a four hour run under load with a node stopped every thirty seconds and kept down for ten - 329,631 acknowledged writes, 111,516 of them taken while a node was gone, and none of them lost across 479 restarts - along with 51 bytes of append log per write, the Raft log a node too busy restarting never truncates, and a make soak target that reproduces all of it ([#246](https://github.com/skyoo2003/kvs/issues/246))
* The release page now covers what one tag actually produces, the checks worth running before pushing it, and an upgrade section measured by running v0.1.1 and v1 side by side - the library, the CLI, and gRPC unchanged, an HTTP 405 now carrying a JSON body, a RESP listener appearing on loopback, and no data directory to migrate because v0.1.1 never wrote one ([#248](https://github.com/skyoo2003/kvs/issues/248))
* The clustering page no longer claims that a node restarting without pause grows the heap. Reproducing the original condition for four hours - the same 479 restarts, each node back the instant it stopped - moved the heap from 6.7MB to 7.0MB where the earlier figure implies about 130MB; that figure came from the harness before its load and its fault injection were separated. The Raft log still grows, and instant restarts reached 202MB a node against 126MB, because it grows with writes. `SOAK_DOWN=0` reproduces the condition and the cluster soak now fails if the heap more than doubles ([#248](https://github.com/skyoo2003/kvs/issues/248))
* The durability and clustering page now carries numbers from a four hour run with a node stopped every thirty seconds: 329,631 acknowledged writes, none lost across 479 restarts, and 51 bytes of append log per write. `make soak` reproduces it ([#246](https://github.com/skyoo2003/kvs/issues/246))
* The release page now covers what one tag actually produces, the checks worth running before pushing it, and an upgrade section measured by running v0.1.1 and v1 side by side ([#248](https://github.com/skyoo2003/kvs/issues/248))
* The clustering page no longer claims that a node restarting without pause grows the heap; repeating the run moved it from 6.7MB to 7.0MB where the old figure implies about 130MB. The Raft log still grows with writes, and the soak now fails on a doubled heap ([#248](https://github.com/skyoo2003/kvs/issues/248))
### Misc
* Migrate golangci-lint config to v2 and pin the linter to v2.12.2 in CI ([#229](https://github.com/skyoo2003/kvs/issues/229))
6 changes: 6 additions & 0 deletions content/docs/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ changie batch v1.2.3 # fold every fragment into changes/v1.2.3.md
changie merge # prepend that file to CHANGELOG.md
```

**A fragment is one sentence.** Two if the second earns it — a consequence, a caveat, or a number
someone would otherwise have to ask for. Write what a reader upgrading can observe, not how it was
built. `.changie.yaml` caps a body at 280 characters, which is about where a second sentence ends;
hitting the cap means the fragment is carrying documentation, and documentation belongs on a page
the notes can link to.

`changie batch` empties `changes/unreleased/`, so **read what it produced before merging.** Two
things are worth looking for, both of which have happened here:

Expand Down