Add HINCRBY, HINCRBYFLOAT and HSETNX hash commands#25
Conversation
There was a problem hiding this comment.
Pull request overview
Adds Redis-compatible hash counter and conditional write commands to RAMen, extending the existing hash feature set to support common counter/update patterns.
Changes:
- Implemented
HSETNX,HINCRBY, andHINCRBYFLOATin the store layer (including overflow / NaN / Inf handling). - Registered new commands and added server-side handlers for the RESP API surface.
- Added store + server integration tests and updated the command documentation.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/store/string.go | Introduces new shared error values used by hash increment logic. |
| internal/store/hash.go | Implements HSetNX, HIncrBy, and HIncrByFloat in the storage layer. |
| internal/store/store_test.go | Adds unit tests covering new hash semantics and edge cases (wrong type, overflow, NaN/Inf). |
| internal/server/cmd_hash.go | Adds command handlers for HSETNX, HINCRBY, HINCRBYFLOAT. |
| internal/server/commands.go | Registers the new hash commands. |
| internal/server/server_test.go | Adds end-to-end server tests for new commands and error paths. |
| docs/commands.md | Documents the new hash commands and provides examples. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // normalize negative zero so we return "0", not "-0", like Redis | ||
| if cur == 0 { | ||
| cur = 0 | ||
| } | ||
| out := strconv.FormatFloat(cur, 'f', -1, 64) | ||
| h[field] = out |
| // ErrIntegerOverflow is returned when an increment would overflow int64. | ||
| var ErrIntegerOverflow = errors.New("ERR increment or decrement would overflow") | ||
|
|
||
| // ErrNotFloat is returned when a value cannot be parsed as a finite float64. | ||
| var ErrNotFloat = errors.New("ERR value is not a valid float") | ||
|
|
||
| // ErrFloatOverflow is returned when a float increment would produce NaN or Infinity. | ||
| var ErrFloatOverflow = errors.New("ERR increment would produce NaN or Infinity") |
There was a problem hiding this comment.
Thanks for this @tchalikanti1705 , really solid work. 🙌
Pulled it down and ran everything locally: tests pass (including your 6 new ones), gofmt and go vet are clean, and it slots right into the existing HSet conventions. Snapshot persistence picks up the new fields for free too.
What stood out is how careful the edge cases are: overflow/underflow, NaN/Inf, negative-zero, WRONGTYPE, and asserting the field stays untouched when an op errors. That last one is the bit most people forget.
One tiny future-cleanup thought (not blocking): the get-or-create-hash block now repeats across the four write methods, so a small hashForWrite helper could tidy it up someday. Totally fine as-is since it matches the current style.
Approving and merging. Nice contribution! 🚀
Summary
Adds the
HINCRBY,HINCRBYFLOAT, andHSETNXhash commands so RAMen matches Redis for common hash counters and conditional field writes.Related Issue
Closes #5
Changes
HSETNX <key> <field> <value>— sets a hash field only when it does not already exist and returns1or0HINCRBY <key> <field> <increment>— increments an integer hash field, treating missing fields as0HINCRBYFLOAT <key> <field> <increment>— increments a float hash field and rejects invalid, NaN, and infinite valuesHSetNX,HIncrBy, andHIncrByFloatmethods ininternal/store/hash.gointernal/server/cmd_hash.goandinternal/server/commands.gointernal/store/store_test.goandinternal/server/server_test.go, and documented the commands indocs/commands.mdChecklist
go test ./...)go fmt ./...)