Release/v1.60.0 - #4107
Merged
Merged
Conversation
…-logger-sql-mock-3813 fix(cron): join in-flight jobs on Stop and skip jobs with no logger
…ng (#3864) v2.3.1 declares `using: node20`, so every Linting Party run emits the Node.js 20 deprecation warning. Upstream fixed it in 0c7f19c ("chore: run on the node24 runtime"), but no release carries it yet, so pin the SHA. Revert to a tag once ls-lint cuts a release with the node24 runtime. Co-authored-by: Aryan Mehrotra <aryanmehrotra2000@gmail.com>
… 18 (#3870) (#3871) Node 18 "Hydrogen" reached end-of-life on 2025-04-30 and receives no security patches, including for the bundled OpenSSL and undici. The prod and stage website workflows build and deploy gofr.dev — they run `yarn install --frozen-lockfile` and `yarn refresh-data` against the network while holding the GAR deployment key and `packages: write`. Targets 24.x rather than the 22.x the issue suggested: v22 has been in maintenance since 2025-10-21 while v24 is the Active LTS through 2028-04-30. Verified `yarn install --frozen-lockfile` is green on node:24-alpine against the website's current lockfile. Both files changed together so prod and stage don't drift. Co-authored-by: Aryan Mehrotra <aryanmehrotra2000@gmail.com>
…cate the request objects (#3974)
# Conflicts: # pkg/gofr/version/version.go
4 tasks
Umang01-hash
approved these changes
Aug 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release v1.60.0
🚀 Features
🔹 Text Embeddings for LLMs
GoFr's
aipackage now supports text embeddings.Embedis a method on the LLM, alongsideChatandStream, with the same tracing and token metrics. Vectors are placed by the provider's reportedindexrather than by response position, so a backend answering out of order cannot pair an input with the wrong vector.A chat-only model reports
ai.ErrEmbedNotSupportedand an unregistered oneai.ErrLLMNotConfigured, so a misconfiguration surfaces as an ordinary error rather than a panic.🔹 LLM Concurrency Limiting
llm.Client.MaxConcurrentRequestscaps in-flight provider requests, for local models or tight rate-limit tiers where concurrent handler calls queue up and inflate tail latency. ExcessChat,EmbedandStreamcalls block until a slot frees, honoring their context deadline. Health probes are now single-flight. The default of0is unlimited.🔹 Opt-In Trie Router
GOFR_ROUTER=triereplaces mux's linear route scan with a radix trie, handing the final match decision back toRoute.Match. mux stays the registry, somux.Vars, route templates and registration are unchanged.Gains start around 5-10 routes and cost 2 extra allocations per request. Routes the trie cannot safely index, and every non-match, fall back to mux, so an indexing mistake costs latency rather than 404ing a live route. Default behavior is unchanged. See Routing Performance.
🔧 Enhancements
🔹 Reduced Per-Request Allocations
The request hot path now uses 8 fewer allocations and 225 fewer bytes per request (49 → 41 allocs, 3915 → 3690 B), measured through a real GoFr handler. Four changes, none of which alters output:
handleRequestLogbuilt the entry, formatted a timestamp and resolved the client IP before the logger decided whether to emit it. An optionalLogEnabled()lets the middleware ask first, saving 193 B and 4 allocs per request atLOG_LEVEL=NOTICEand above. The defaultINFOis unaffected, since the request log is written at INFO.Context,RequestandResponderare created together and discarded together, so they now live in one struct instead of three separate heap objects.handler.ServeHTTPresolved a 32-character trace ID on every request for a consumer that only runs when a handler returns an error. It is built on the error path now.Alongside those,
remoteLogger.LogEnabledno longer takes a per-requestRWMutex.RLockon a field it never read, and the log level moved to anatomic.Int64— clearing 3 data races inpkg/gofr/logging/remoteloggerunder-race.The first change moves work from per-request to per-log-call, so it breaks even at 3 discarded log lines per request. Output is byte-identical to v1.59.0: 348 request cases and every log line at all 7 levels were differenced with no changes.
🛠️ Fixes
CORS Origin Bypass — A differently-cased
access-control-allow-originkey slipped past both the header guard andparseOrigins, so a configured allow-list could answer an untrusted origin with*. Header names are now canonicalized at construction.CORS Data Race —
append(routes, "OPTIONS")wrote into the backing array of the router'sRegisteredRoutes. Reported 3 data races under-race, now clean.Content-TypeMatching — Matching was case-sensitive and untrimmed, soApplication/JSONandapplication/json ; charset=utf-8bound nothing. Now parsed per RFC 9110, withapplication/octet-streamaccepted alongsidebinary/octet-stream.BindSilently Discarded Bodies —Bindinto a non-pointer, andapplication/octet-streaminto a target that is not[]byte, unmarshalled into a throwaway and returnednil, so the handler saw success and no data. Both are reported now, which means a handler that returns the error responds500where it previously returned201with an empty struct. GoFr's ownbinary/octet-streamspelling already errored this way; the RFC spelling and the case variants now match their siblings.BindNil Body Panic — A request built by hand, as in a handler unit test, panicked inio.ReadAll. An absent body is now treated as empty.Dropped Response Headers —
Headersset on a*response.Responsenever reached the wire.gRPC Shutdown Race —
grpcServer.serverwas written and read across goroutines unsynchronized, and aShutdownreading a stalenilreported success without stopping anything, letting the process exit with the server still serving.Cron Data Loss on Shutdown —
Stop()returned as soon as the ticker halted without joining running jobs, so a job mid-work carried on against datasourcesApp.Shutdownwas already closing and failed withsql: database is closed, losing the rest of a half-written batch.Stop()now joins in-flight jobs, so it blocks for as long as the job runs;App.Shutdowncaps that at its own context deadline. A container with no logger also segfaulted injob.run.Flaky Example Tests — All 31
time.Sleepcalls replaced with real readiness signals, andusing-migrationsnow owns its own MySQL and Redis databases.📦 CI & Dependencies
Dependency Updates — 26 Dependabot PRs consolidated: OpenTelemetry 1.45, prometheus exporter 0.67,
go-redis9.22.0 (withMockRedisregenerated), plusaws-sdk-go-v2,clickhouse-go,cloudsqlconn,x/cryptoandsqlite. Note that the prometheus exporter now formats label values viaattribute.Value.String(), so aStringSlicerenders as[a,b]rather than["a","b"].CI Hardening — Concurrency groups and job timeouts, actions pinned by commit SHA, service health checks in
Example-Unit-Testing, and Node 24 in the website workflows. Dependabot now coverspkg/gofr/metrics/exporters/gcpand all 11 Dockerfiles, one of which had been left onalpine:3.14.Full changelog: v1.59.0...v1.60.0