Migrating from v3 to v4¶
v4 changes the module path, three exported symbols and the RediSearch schema. Everything else is source-compatible with v3.2.0.
Update the import path — the package name is unchanged:
1. Message.Url → Message.URL¶
Go initialisms are upper-case. The JSON tag stays url, so nothing on the
wire, in Redis JSON or in the RediSearch index changes — this is a
compile-time rename only.
Mechanical fix across a service:
2. SendToHomerun returns a Response¶
// v3
answer, resp, err := homerun.SendToHomerun(dest, token, body, false)
fmt.Println(resp.Status, string(answer))
// v4
resp, err := homerun.SendToHomerun(dest, token, body, false)
fmt.Println(resp.Status, string(resp.Body))
v3 returned the raw *http.Response after already reading and closing its body,
so a caller that tried to read Body got nothing and one that closed it was
closing a closed body. Response carries what a caller can actually use:
type Response struct {
StatusCode int
Status string
Header http.Header
Body []byte
}
func (r Response) OK() bool // 2xx
A non-2xx answer is still not an error — err reports whether the request could
be made and read at all. Check resp.OK() for the endpoint's verdict.
The same applies to SendToHomerunContext.
3. RediSearch: timestamp is NUMERIC and carries the event time¶
Two fixes in the schema StoreInRediSearch creates:
timestampisNUMERIC, notTEXT. RediSearch cannot range-query TEXT, so@timestamp:[1757836800 +inf]— "everything since yesterday" — was not expressible. Sorting worked, filtering did not.- The indexed value is
Message.Timestamp, nottime.Now(). v3 recorded the moment of indexing. In a queue-backed system that differs from the event time on every retry, backlog and replay, and the original was then unrecoverable.
A missing or unparseable Message.Timestamp falls back to the current time and
logs a warning (install a logger with homerun.SetLogger to see it).
This needs the index recreated¶
A field's type cannot be changed in place, and the library only checks whether the index exists. An index created by v3 keeps the old TEXT schema, so the range query keeps failing until you recreate it:
redis-cli FT.DROPINDEX <index> # keeps the documents
# restart the service; StoreInRediSearch recreates the index with the new schema
FT.DROPINDEX <index> DD also deletes the indexed documents — only use it if you
intend to lose the history.
4. StoreInRediSearch is deprecated, and now refuses an incompatible index¶
StoreInRediSearch maintains a second, hash-based copy of every message in
its own key. If your index is defined ON JSON, a hash written into it is stored
but never indexed — FT.SEARCH will not find it and num_docs does not move.
In v3 this failed silently: the call returned nil, and the orphaned key had
no TTL, so nothing that prunes by search result would ever delete it. That is not
hypothetical — homerun2-omni-pitcher creates its index ON JSON at startup, so
every call against it produced exactly that.
v4 detects the mismatch and returns an error instead:
redisearch index homerun-idx indexes JSON keys, but StoreInRediSearch writes
hashes: documents would be stored but never indexed. Recreate the index with
ON HASH, or index the Redis JSON documents written by Enqueue instead
If you index the Redis JSON documents (the ON JSON case), you do not need
StoreInRediSearch at all: Enqueue already writes the JSON document, and an
index over those keys picks it up automatically with the correct event
timestamp. Drop the call.
If you own a dedicated ON HASH index, StoreInRediSearch keeps working with
the fixes above.
It is deprecated either way and scheduled for removal in v5.
Making the JSON index range-queryable¶
The JSON documents carry timestamp as an RFC3339 string, which has the same
range-query limitation: RediSearch can only declare it TEXT.
Since v4.5.0 Enqueue also writes the event time as Unix seconds into every
document, under timestamp_unix (homerun.RediSearchTimestampField). Declare it
NUMERIC in the index and time windows become queries:
FT.CREATE messages ON JSON SCHEMA ... $.timestamp_unix AS timestamp_unix NUMERIC SORTABLE
FT.SEARCH messages "@timestamp_unix:[1757836800 +inf]"
FT.AGGREGATE messages "@timestamp_unix:[1757836800 1757923200]" GROUPBY 1 @severity REDUCE COUNT 0 AS n
The value is Message.Timestamp, the time the event happened; a missing or
unparseable one falls back to the time of Enqueue. The document's other fields
are unchanged, and readers decoding it into a Message ignore the extra field.
An existing index keeps its schema - the services creating it only check that it exists. Once they declare the field, recreate it once per environment:
redis-cli FT.DROPINDEX messages # without DD: the documents stay
# restart the service that creates the index
Documents written before v4.5.0 have no timestamp_unix, so range queries do not
match them until they age out. Until the index declares the field, consumers
filtering by time have to fetch and compare in Go, which is what
homerun2-scout's retention does.
5. redisearch-go v1 → v2¶
Internal: the library now uses github.com/RediSearch/redisearch-go/v2. This
only affects you if your own code imports redisearch-go directly.