4 hours ago
wails

Wails v3.0.0-beta.24

Wails v3 Beta Release - v3.0.0-beta.24

Security

  • Restrict GITHUB_TOKEN permissions in build and publish workflows to follow the principle of least privilege
  • Fix path traversal and symlink escapes in the screen example asset middleware
  • Keep setup wizard dependency installation functional while restricting it to approved package-manager commands

🤖 This is an automated nightly release generated from the latest changes on master.

Installation:

go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-beta.24

⚠️ Beta Warning: This is pre-release software. The API is stable, but you may still encounter issues before the final 3.0 release.

6 hours ago
pdfcpu

v0.16.0-rc.1

pdfcpu v0.16.0-rc.1

This release candidate is available for testing.

Please try it with your existing workflows and report regressions before the final v0.16.0 release.

Highlights

  • Updated Go API — Explicit contexts for long-running operations, optional progress reporting, and reusable caller-owned configuration.
  • Configuration redesign — Schema-aware loading, explicit initialization and reset, plus read-only and stateless operation.
  • Automation and container preparation — Signal cancellation, safer output replacement, password-file inputs, and verified execution under arbitrary user IDs.
  • Stronger PDF validation — Improved malformed-input handling, graph-traversal safeguards, and compatibility warnings for selected relaxed-validation decisions.
  • Clearer signature validation — Separate reporting of document integrity, certificate trust, revocation, and timestamp evidence.
  • PDF processing fixes — Improved resize orientation, rotated watermarks, form appearances, image handling, and LZW decoding.
  • Smaller Go module — Approximately 94% smaller in the original packaging comparison. Samples and test fixtures remain in Git but are excluded from module downloads.

Requirements and installation

Go applications require Go 1.26 or later and updates to affected API calls. Existing file-backed configurations from v0.15 or earlier require an explicit configuration reset.

go get github.com/pdfcpu/pdfcpu@v0.16.0-rc.1

Before upgrading

Reset legacy file-backed configuration

Existing v0.15 and older config.yml files do not contain the new configuration schema identifier. pdfcpu preserves the file and reports that a reset is required instead of rewriting it automatically.

If the configuration is not customized, run:

pdfcpu config reset
pdfcpu config validate

For an explicit configuration root, use the same root for every command:

pdfcpu --conf /srv/pdfcpu config reset
pdfcpu --conf /srv/pdfcpu config validate
pdfcpu --conf /srv/pdfcpu config inspect

Installed user fonts and trusted certificates are preserved.

Before resetting, back up any customized config.yml.
After resetting, reapply your settings. Installed fonts and trusted certificates are preserved.
New installations and stateless operation with --conf disable need no migration.

See the v0.16 configuration upgrade guide.

Update Go API callers

Change Required action
Long-running operations require a context.Context Pass the request or job context, or context.Background() when cancellation is not needed. Nil contexts are rejected.
Validation and optimization operations take a final *api.ProgressOptions Pass nil when progress events are not needed.
LoadConfiguration() now takes options and returns an error Call api.LoadConfiguration(api.ConfigurationOptions{}) and handle the error.
Context-free and interim WithContext/WithOptions variants were consolidated Use the canonical operation name.

Configurations supplied by an application remain caller-owned and can be reused after an operation. Clone a configuration before applying different settings for another job; do not mutate it concurrently while operations use it. Passing nil loads the default configuration and may initialize files on disk. For stateless applications, explicitly load api.ConfigurationModeStateless and pass the returned configuration.

See API installation and usage and the v0.16 migration guide for before/after examples.

Configuration and runtime

Explicit configuration modes

  • Automatic discovers or initializes file-backed configuration for normal CLI and API use.
  • Read-only loads a prepared configuration tree without modifying it.
  • Stateless uses built-in settings and the 14 core PDF fonts without accessing configuration files, user fonts or the local certificate store.

The CLI selects stateless mode with --conf disable.
Applications select a mode through api.ConfigurationOptions.
Configuration root precedence is the explicit flag, PDFCPU_CONFIG_ROOT, then the operating-system default.
Normal CLI PDF commands have no read-only-mode flag: prepare the complete tree first and mount it read-only.
Go applications can select api.ConfigurationModeReadOnly to load existing configuration without modifying files.

The new commands are:

pdfcpu config init
pdfcpu config list
pdfcpu config inspect [--json]
pdfcpu config validate
pdfcpu config reset

Use pdfcpu config inspect for configuration paths and effective policy.

Cancellation and transactional output

The CLI responds to Ctrl+C, SIGINT and SIGTERM. The first signal requests a clean stop; a second signal terminates immediately. Go callers control cancellation through the context passed to the operation.

Cancellation is cooperative, so a large operation may take a moment to reach a safe stopping point. File-producing operations stage output before publication. When cancellation or an ordinary write failure occurs before publication, an existing destination is preserved and unfinished temporary output is removed.

Stdin PDF input and merged form multi-fill output to stdout use the operating-system temporary directory. Replacement files are staged beside their destination. On supported Unix systems, replacement preserves the destination group or fails before publication.

Password files

Use --upw-file or --opw-file wherever the corresponding literal password flag is accepted. Supply a password either directly or through a file, not both. Password files cannot use stdin.

One trailing LF or CRLF is ignored. Other spaces and line endings remain part of the password. An empty file supplies an empty password, except where a non-empty owner password is required.

Password changes can replace their positional old/new password pair with:

Command Old password New password
changeupw --upwold-file --upwnew-file
changeopw --opwold-file --opwnew-file

Both file options for a password change must be supplied together.

Resource and network policy

  • maxInputBytes optionally limits each PDF input, including stdin spooling. Zero remains unlimited.
  • maxObjectBytes exposes the existing per-object reader buffer limit. Its default remains 64 MiB.
  • Offline mode now consistently covers remote images, link validation and live CRL/OCSP requests.
  • Outbound image and revocation requests reject loopback, private, link-local, multicast, unspecified and selected special-purpose destinations by default. Trusted private revocation hosts can be configured explicitly.

These settings limit individual inputs or operations; they are not a total memory, disk or job-time budget.

Validation

Compatibility warnings

Compatibility-warning coverage has substantially expanded. Relaxed validation reports selected conditions that strict validation would reject, indicating whether content was accepted, skipped or repaired in memory. These warnings are available through the CLI and structured API reports; --quiet suppresses CLI warnings.

Go callers can obtain the same ordered report through ValidateWithReport, ValidateFileWithReport and ValidateContextWithReport. Existing error-only validation APIs remain available.

The v0.16.0 coverage is intentionally bounded and will be extended gradually.
Right now it covers the parser vertical slice and selected Type 1 font, remote destination, annotation, Type 3 function, simple-font width and Indexed-image mask compatibility paths changed for this release. Other existing relaxed parser, reader and validator fallbacks may not produce a structured notice. The absence of a notice does not prove that relaxed validation followed the strict path.

Strict validation describes the checks pdfcpu currently implements; it does not certify complete ISO 32000 compliance or prove that no bounded low-level reader recovery occurred.

Expanded validation and malformed-input handling

Stronger validation and safer handling of malformed PDFs, including improved bounds checks, cycle detection and error reporting. Relaxed mode adds targeted compatibility exceptions while strict validation retains its requirements.

Signature-validation evidence

Signature validation now separates established evidence from the overall local assessment. Compact output includes an Integrity: line. Full output groups cryptographic signature, signed-content digest, profile, signer certificate, certificate path, revocation and timestamp evidence before presenting the assessment.

unknown means that evidence was unavailable, inconclusive or not checked; it does not mean success.

Document timestamps receive the supported local RFC 3161 CMS-signature, message-imprint, profile, TSA-certificate and configured-local path checks. Embedded signature timestamp tokens are located and parsed, including their generation time, but are not yet authenticated. Timestamp presence alone does not establish PAdES B-T.

pdfcpu reports local technical evidence.
It does not make enterprise-policy, legal-validity, eIDAS, qualified-signature or complete long-term-validation decisions.
PAdES classification remains limited to the supported B-B result.

See signature validation for the complete output and current limitations.

Distribution and constrained environments

The Go module now excludes samples and test fixtures while retaining all runtime resources (#1449).
This release prepares pdfcpu for container deployment; an official image is planned for v0.17.

Compatibility summary

  • Existing v0.15 and older file-backed configuration requires an explicit reset.
  • Long-running Go APIs require a non-nil context under their canonical names.
  • Validation and optimization APIs listed above require the final progress argument.
  • Strict validation may reject malformed structures that earlier releases did not check.
  • Relaxed compatibility warnings do not yet cover every pre-existing fallback.
  • Cancellation takes effect at checkpoints, so some operations may not stop immediately.

Fixed issues

#1407, #1444, #1449, #1457, #1460, #1461, #1465, #1466, #1467, #1470, #1472, #1473, #1474, #1477, #1479, #1484, #1485, #1487.

2 days ago
grpc-go

Release 1.84.0

Behavior Changes

  • stats/otel: The grpc.lb.pick_first.* metrics have been removed and replaced with grpc.subchannel.* metrics. See gRFC A94 for more details. (#9215)

New Features

  • xds: Add support for contains_match in route header matchers. (#9223)
    • Special Thanks: @nvxbug

Bug Fixes

  • client: Fix a bug where a ClientConn could get permanently stuck in IDLE when an RPC was canceled during stream creation. Previously, such cancellations triggered stream cleanup twice, corrupting the channel's idleness state and causing subsequent RPCs to fail with deadline exceeded errors. (#9191)
    • Special Thanks: @utkuozdemir
  • client: Fix a bug where non-gRPC HTTP responses ending with an empty DATA frame failed the RPC with status code Internal instead of preserving the HTTP-mapped status code and response body. (#9217)
    • Special Thanks: @chengxilo
  • credentials: Validate metadata returned by per-RPC credentials, failing the RPC with status code Internal if invalid keys or values are found. Previously, invalid metadata from credentials was sent to the server in outgoing HTTP/2 requests. (#9202)
    • Special Thanks: @buger
  • credentials/sts: Prevent potential token leakage by disallowing HTTP redirects during STS token exchange. Previously, 3xx redirects were followed automatically, replaying the request body containing authentication tokens to the redirect destination. (#9299)
    • Special Thanks: @nvxbug
  • randomsubsetting: Ignore endpoints that contain no addresses. Previously, this could cause the policy to panic while computing hashes. (#9259)
    • Special Thanks: @fallintoplace
  • stats/otel: Ensure method names are populated in trace spans when metrics are disabled. Previously, running with tracing enabled and metrics disabled resulted in server trace spans lacking the RPC method name (recording only "Recv."). (#9262)
  • transport: Return io.ErrUnexpectedEOF when EOF is encountered after partial header or message body reads. Previously, partial reads could return a plain io.EOF, failing to distinguish truncated data from a clean end of stream. (#9204)
    • Special Thanks: @buger
  • transport: Validate metadata supplied by balancers (in PickResult.Metadata) and resolver addresses, failing the RPC with status code Internal if invalid keys or values are found. Previously, invalid metadata from these sources was sent to the server in outgoing HTTP/2 requests. (#9203)
    • Special Thanks: @buger
  • xds: Fix a rare corner case that could prevent a cluster from being removed when it is no longer in use. (#9140)
  • xds: Fix panic during route matching for routes containing header matchers with empty exact_match strings. (#9223)
    • Special Thanks: @nvxbug
  • xds: Reject routes containing header matchers with empty prefix_match or suffix_match strings. Previously, this caused a panic during route matching. (#9223)
    • Special Thanks: @nvxbug
  • xds: Fix EDS drop policies being applied at a much lower rate than configured due to an integer overflow. (#9257)
    • Special Thanks: @nvxbug
  • xds: Reject EDS resources containing drop policies with unsupported denominators. Previously, such resources caused the client to panic when calculating drop rates. (#9218)
    • Special Thanks: @nvxbug
  • xds/rbac: Reject RBAC configurations containing nested Principal or Permission rules with :scheme or grpc- prefixed header matchers. Previously, such configurations could cause DENY policies to fail open. (#9258)
    • Special Thanks: @nvxbug
  • xds/rbac: Rewrite host header matchers to :authority in nested Principal and Permission rules. Previously, this rewrite only applied to top-level rules, causing nested host matchers to never match incoming requests and DENY policies to fail open. (#9258)
    • Special Thanks: @nvxbug
  • xds/rbac: Reject CidrRanges with an unset prefix length. Previously, an omitted prefix_len field caused a panic during RBAC configuration parsing. (#9250)
    • Special Thanks: @nvxbug

Performance Improvements

  • transport: Avoid a heap allocation when flushing shared write buffers. (#9233)
    • Special Thanks: @zaidoon1
  • credentials/alts: Support dynamic frame size negotiation and add the GRPC_GO_EXPERIMENTAL_ALTS_MAX_FRAME_SIZE environment variable (default 4KiB, max 512KiB) to configure the maximum ALTS record frame size. (#9268)
3 days ago
lego

v5.5.1

lego is an independent, free, and open-source project, if you value it, consider supporting it! ❤️

Everybody thinks that the others will donate, but in the end, nobody does.

So if you think that lego is worth it, please consider donating.

For key updates, see the changelog.

Changelog

  • 6a1a17869e117f615efb4b8278f9b324816ea76e fix: add KeyType to RenewOptions
3 days ago
lego

v5.5.0

lego is an independent, free, and open-source project, if you value it, consider supporting it! ❤️

Everybody thinks that the others will donate, but in the end, nobody does.

So if you think that lego is worth it, please consider donating.

For key updates, see the changelog.

Changelog

  • f38820ab20d9d75fc4841f406308568370c01776 Add DNS provider for DNSMint (#3264)
  • 2195984fbf933f6c4e9aeaa520890fb2740c3442 Add DNS provider for FENO (#3251)
  • 3c5302df3fd1b79c7c50b7e53f18d03aa3d458d0 Add DNS provider for Myra (#3236)
  • 9e14d2e7063f8dd58e61aca785c05fead163d3d4 Add DNS provider for Webglobe (#3277)
  • a1a6bb007141530a7e6bdcf1350816d3e8dd2d5f docs: fix some examples (#3276)
  • ccd2703a4c55ac3ab15f275c06968c1fd81f68ad dynadot: fix request bodies (#3268)
  • 4a044bee81dc47b0ca86b585ceadda2b9ddf1db6 feat(cli): display account URL in the human-readable accounts list (#3244)
  • d638164dc4f1d89b2f5c2dd6452d5db7c39196ac feat(lib): add UseARICertID to RenewOptions (#3273)
  • 7111f645a8b9e7d7cabfcb259c3d65e4986970ea fix(cli): Punycode encoding domains for during renewal (#3257)
  • fbb3aff68248663405ab8588ea84f23783934e42 fix: FormattableDuration on 32bits arch (#3255)
  • 0671c9ba21ff91cfe6faa50cca0f3c26827cdbb0 gcloud: support access token (#3220)
  • f6b0685aa5a4766ed69cd8ec1f512a1563dc9983 websupport: fix get records filter (#3259)
  • 1ea18096e98a19c7e477628ce839d9e0dfa33a3e xinnet: remove URL trailing slash (#3270)
4 days ago
wails

Wails v3.0.0-beta.23

Wails v3 Beta Release - v3.0.0-beta.23

Added

  • Allow overriding the GTK application ID on Linux in PR by @overlordtm

Fixed

  • Linux single-instance claims bus name under app's own ID in PR by @overlordtm
  • Cancel aborted custom-scheme requests on Linux in PR by @leaanthony

🤖 This is an automated nightly release generated from the latest changes on master.

Installation:

go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-beta.23

⚠️ Beta Warning: This is pre-release software. The API is stable, but you may still encounter issues before the final 3.0 release.

6 days ago
wails

Wails v3.0.0-beta.22

Wails v3 Beta Release - v3.0.0-beta.22

Added


🤖 This is an automated nightly release generated from the latest changes on master.

Installation:

go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-beta.22

⚠️ Beta Warning: This is pre-release software. The API is stable, but you may still encounter issues before the final 3.0 release.

7 days ago
wails

Wails v3.0.0-beta.21

Wails v3 Beta Release - v3.0.0-beta.21

Added

  • Serve Wails v3 documentation with M-Press in PR by @leaanthony

Fixed

  • Parse JSON slug values from MPD frontmatter for changelog generation in PR by @leaanthony
  • Updater clears helper env vars and relaunches original target after backup failures in PR by @cnmax
  • Start default signal handler during App.Run in PR by @leaanthony
  • Windows menu handles nil menus, frees replaced resources, and redraws the menu bar in PR by @taliesin-ai
  • Restore MSIX packaging for fresh projects using shared YAML configuration in PR by @leaanthony
  • Fix generated JavaScript and TypeScript bindings failing to load when generic model creators reference later helper declarations, and prevent stack overflows when creating mutually dependent generic models (#6062)

🤖 This is an automated nightly release generated from the latest changes on master.

Installation:

go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-beta.21

⚠️ Beta Warning: This is pre-release software. The API is stable, but you may still encounter issues before the final 3.0 release.

9 days ago
redis

9.23.0-beta.1

This is a beta release. You can upgrade without changes to your code. The release adds three primary features:

  • An experimental full-duplex mode for the automatic pipeliner.
  • Refresh and miss-coalescing functions for client-side caching.
  • Better distribution of cluster reads under latency-based routing.

The release also contains many stability fixes and robustness fixes.

⚠️ This release changes one default behavior. Each Client (this includes failover clients and cluster node clients) now creates a small, dedicated pipeline connection pool (#4002). Pipeline, TxPipeline, and autopipeline operations use this pool. These operations do not compete with regular commands for main-pool connections. The pool supplies burst capacity only:

  • The pool does not dial a connection before the connection is necessary.
  • An unused pool holds zero connections. The idle cost is zero.
  • If the pool is full, an operation immediately uses the main pool.

Set PipelinePoolSize: -1 to get the previous single-pool behavior.

🚀 Highlights

Full-Duplex Auto-Pipelining (Experimental)

Set AutoPipelineOptions.FullDuplex to enable the full-duplex mode of the automatic pipeliner. The default mode sends one batch for each round trip. The full-duplex mode is different: the engine holds one pipeline-pool connection, and a writer goroutine and a reader goroutine move the ordered command stream in the two directions at the same time. On a link with high latency, each command completes in approximately one round-trip time (RTT) on a single connection. A test on a 50 ms WAN profile measured ~389k operations/s at 52 ms p50. The half-duplex ordered path measured ~207k operations/s at 116 ms.

The mode operates on the two faces of a standalone Client: AutoPipeline() and AsyncAutoPipeline(). The mode also operates natively on a ClusterClient if the routing goes to masters only. A child engine for each master sends each command to the node that owns the command's slot. The engine obeys MOVED and ASK redirects and retryable replies (LOADING, READONLY, ...) through the usual cluster redirect procedure. If replica routing is set (ReadOnly, RouteByLatency, or RouteRandomly), the autopipeliner uses the half-duplex shard flushers, which obey the shard picker. Config().FullDuplex reports the mode that is in effect.

These options tune the mode:

  • FullDuplexWindow — the maximum number of commands in flight (backpressure).
  • FullDuplexIdleTimeout and FullDuplexMaxHold — control when the engine returns the held connection to the pool. The pool hooks then do the re-authentication and the maintenance-notification handoffs.
  • FullDuplexFastSubmit — an optional fast submit path for many producers on low-RTT links.

The full-duplex path supports blocking commands, Options.Limiter (one admission for each written batch), per-command hooks, OTel metrics, retry budgets (the client never sends a NoRetry command again after the command is on the wire), and seamless maintenance handoffs. The stream model causes these limits:

  • A hook can monitor a command. A hook cannot stop a command. A ProcessHook that returns without a call to next does not cancel the command on the full-duplex path. The command is already in the queue of the held connection, and the client sends it. Run policy hooks and kill-switch hooks on a plain client or on the half-duplex autopipeliner.
  • The order guarantee does not include diverted commands. The commands of one caller keep their order on the shared stream. The engine diverts some commands from the stream: blocking commands, connection-hostile commands, and managed HIMPORT commands. A diverted command has no order relation to the stream. On the async face, wait for the result of a diverted command before you submit a command that depends on it.
  • The fast path does not use the client-side cache. If CSC is enabled, a cacheable command on the full-duplex pipe does not read the cache and does not write to the cache. A subsequent change will correct this.
  • The engine records a duration metric for each reply. Some commands fail before they get to the reader: a lease failure, a limiter denial, retry exhaustion, or a close. These commands cause the error callback, but they do not cause an operation-duration sample.
rdb := redis.NewClient(&redis.Options{
	Addr: "localhost:6379",
	AutoPipelineOptions: &redis.AutoPipelineOptions{
		FullDuplex: true, // stream commands on one held connection, ~1 RTT each
	},
})
defer rdb.Close()

// Deferred face: calls return immediately; result accessors block until executed.
ap, err := rdb.AsyncAutoPipeline()
if err != nil {
	panic(err)
}
cmds := make([]*redis.StatusCmd, 0, 1000)
for i := 0; i < 1000; i++ {
	cmds = append(cmds, ap.Set(ctx, fmt.Sprintf("key:%d", i), i, 0))
}
for _, cmd := range cmds {
	if err := cmd.Err(); err != nil {
		// handle error
	}
}

// Or the blocking face — a drop-in Cmdable where each call blocks like a
// plain client while concurrent callers share the full-duplex pipe:
//	ap, err := rdb.AutoPipeline()
//	val, err := ap.Get(ctx, "key").Result()

The example/autopipeline directory contains a runnable tour of all the autopipeliner faces.

Two related defaults changed. AutoPipelineOptions.MaxBatchBytes now has a default of 128 KiB (before, the size had no limit). This default prevents a write/reply deadlock. It is not a throughput control. The buffers of the pipeline pool also have a default of 128 KiB.

Experimental: the auto-pipelining APIs can change in a minor release.

(#4002) by @ndyakov

Client-Side Caching: Refresh-on-Invalidate and Miss Coalescing

The release adds two functions to the experimental shared-tracking client-side cache. The functions have no effect unless CSC is enabled (#3989) by @ndyakov:

  • Refresh-on-invalidate (Options.ClientSideCacheRefreshOnInvalidate): the client reads recently-read keys again in the background immediately after their invalidation push arrives. The next reader does not pay the cache miss. The client collects invalidated hot keys in a short window and reads them again on a pipelined connection. ClientSideCacheRefreshRecencyWindow sets which entries count as recently read. ClientSideCacheInvalidationBatchWindow collects invalidation-driven cache deletes into background batches; without it, the connection reader applies each delete inline.
  • Miss coalescing (Options.ClientSideCacheCoalesceMisses): the client pipelines concurrent cache-miss reads onto one tracked connection. Each miss keeps the caller's own per-key command. The client does not rewrite the commands to MGET, so the function is safe on a cluster. The client writes each reply to the cache with the tracking generation of the connection. The server can thus invalidate each entry.

These options have the same requirement as the other CSC options: the built-in cache (ClientSideCacheConfig, or ClientSideCache set to a *LocalCache). The client ignores the options if a custom Cache implementation is set.

Better Distribution for Latency-Based Cluster Read Routing

RouteByLatency selects the node with the strictly minimum latency. The latency measurement has noise: the value is the mean of ten pings, and the client refreshes it at most each 10 s. Thus all clients can select the same node from a set of nodes that have almost equal latency. A production system showed this problem: the GET rates across a five-replica set in one availability zone had a 590x spread.

The new ClusterOptions.RouteByLatencyTolerance widens the selection to each node with a latency in the tolerance above the fastest node. The client distributes reads across these nodes with the round-robin procedure of the ShardPicker. A node in a different availability zone stays outside a sensible tolerance, so zone locality is kept. The default is zero, which keeps the strict-minimum behavior. The option is also on FailoverOptions, where it applies to clients from NewFailoverClusterClient. The plain NewFailoverClient does not support latency routing. (#3973) by @jozenstar

The same work corrected a routing defect. The client recorded the nearest healthy node only when that node was also the fastest node overall. A node that fails fast (a refused connection fails fast, so this is frequent) thus hid each healthy node, and the client sent reads to the node that failed. The client now records the healthy minimum separately. (#3994) by @jozenstar

✨ New Features

  • Full-duplex auto-pipelining: AutoPipelineOptions.FullDuplex, with FullDuplexWindow / FullDuplexIdleTimeout / FullDuplexMaxHold / FullDuplexFastSubmit. Available on standalone clients and cluster clients (#4002) by @ndyakov
  • Dedicated pipeline pool by default: PipelinePoolSize has a default of DefaultPipelinePoolSize (10) on each client. If the pool is full, an operation immediately uses the main pool. Set -1 to disable the pool (#4002, #3959) by @ndyakov
  • CSC refresh-on-invalidate and miss coalescing: Options.ClientSideCacheRefreshOnInvalidate (with ClientSideCacheRefreshRecencyWindow / ClientSideCacheInvalidationBatchWindow) and Options.ClientSideCacheCoalesceMisses (#3989) by @ndyakov
  • RouteByLatencyTolerance: distributes reads across nodes that have almost equal latency, on cluster clients and on NewFailoverClusterClient (#3973) by @jozenstar
  • AutoPipeliner.WaitClosed: blocks until the drain of the accepted commands completes, and returns the drain result. Use it in a wrapper that must not close shared pools while a flush is in progress (#3998) by @ndyakov
  • CMSInfo.CellSize: contains the cell-size field of CMS.INFO in Redis 8.12 (#4010) by @elena-kolevska

🐛 Bug Fixes

  • Cluster read routing: the client records the nearest healthy node separately from the overall minimum. A node that fails fast does not hide the healthy nodes (#3994) by @jozenstar
  • Probabilistic *.INFO forward compatibility: the parsers for BF.INFO / CF.INFO / CMS.INFO / TOPK.INFO / TDIGEST.INFO skip unknown fields and do not return an error. Redis 8.12 adds cell size to CMS.INFO (#4010) by @elena-kolevska
  • NewClient panic leak: a panic during construction (for example, a maintnotifications failure in ModeEnabled) does not leak the connection pools that already exist. A typed-nil pool cannot hide the initial panic (#4003) by @ndyakov. The same guards are applied to NewFailoverClient (#4002)
  • File-descriptor leak on rejected connections: Conn.Close does the socket teardown and the unsubscribe/CSC callbacks when the connection is already CLOSED. Before, init and auth failures collected open descriptors. The transport now closes exactly one time for each socket generation (fixes #3982) (#3985) by @ndyakov
  • Global logger races: atomics protect the global Logger and LogLevel. The call-site attribution is correct again (#3988) by @saddamr3e
  • Conn.onClose data race: the close hooks that init installs (onClose and onCscClose) are now atomic against a concurrent Close (#3966) by @saddamr3e
  • Reply-parser hardening: the reply parsers accept zero-length entry arrays (#3995) by @saddamr3e. FTHybridCmd reads the full RESP3 map reply and does not desynchronize the connection (#3956) by @saddamr3e
  • CLIENT INFO forward compatibility: the parser skips unknown client-flag characters. The full reply does not fail (#3977) by @ndyakov
  • GEOSEARCH duplicate args: the command does not send duplicate arguments (#3955) by @mehmettokgoz
  • MSetEX cluster routing: the constructor sets the first-key position. Typed calls thus go to the correct slot (#3984) by @shivamrustagi
  • Maintenance notifications: the client does not do the endpoint DNS detection when the mode is disabled (#3969) by @Phalanyx
  • Autopipeliner Close: a concurrent Close does not block (no re-entrant deadlock). WaitClosed supplies the drain result (#3998) by @ndyakov
  • Buffered-push log noise: the buffered-push-data notice in isHealthyConn shows only at the debug level. CSC invalidations do not fill the log (#3948) by @ndyakov
  • Sentinel teardown order: close hooks run in LIFO order. An autopipeliner drain thus completes before the Sentinel discovery stops. A closed failover client cannot create its Sentinel resources again from a late dial (#4002) by @ndyakov
  • Pipeline desync containment: if a pre-write push-notification drain fails, or if a command encoder panics, the client removes the connection. The client does not return a desynchronized connection to the pool. This applies to the shared Pipeline/TxPipeline path (#4002) by @ndyakov

⚡ Performance

  • Zero-copy scan: Scan gets zero-copy semantics, and the RESP reader does not do unnecessary data conversions (#3972) by @vlady-kotsev
  • Autopipeline straggler hold: the engine limits the hold on queued commands when the pipeline pool has a free connection. Uncached p95 decreased from 111 ms to 65 ms on a 50 ms link. Real-WAN uncached p99 decreased from 314 ms to 177 ms (#3962) by @ndyakov
  • Full-duplex allocations: a ring buffer holds the in-flight queue, and the blocking face uses a pool of batches. Allocations decreased from 770 B/op to 353 B/op at 2048 concurrent callers (#3970, part of #4002) by @ndyakov

🧪 Testing & Infrastructure

  • Fast skip gates: the tests do a TCP probe of each address before the Ping gate. This removes ~1.6 min of dial-retry waits in environments without the full stack (#4001) by @ndyakov
  • Redis Enterprise coverage: the autopipeline suites connect to the RE database and use the suite DB (#3976, #3975). The timing assertions scale to the measured RTT (#3978). The CLIENT INFO tracking-flag assertion does not run behind the RE proxy (#3981) by @ndyakov
  • Security policy: send vulnerability reports to the Redis VDP (#3949) by @ndyakov

👥 Contributors

We thank all the contributors who worked on this release!

@elena-kolevska, @jozenstar, @mehmettokgoz, @ndyakov, @Phalanyx, @saddamr3e, @shivamrustagi, @vlady-kotsev


Full Changelog: https://github.com/redis/go-redis/compare/v9.22.0...v9.23.0-beta.1

9 days ago
go-redis

9.23.0-beta.1

This is a beta release. You can upgrade without changes to your code. The release adds three primary features:

  • An experimental full-duplex mode for the automatic pipeliner.
  • Refresh and miss-coalescing functions for client-side caching.
  • Better distribution of cluster reads under latency-based routing.

The release also contains many stability fixes and robustness fixes.

⚠️ This release changes one default behavior. Each Client (this includes failover clients and cluster node clients) now creates a small, dedicated pipeline connection pool (#4002). Pipeline, TxPipeline, and autopipeline operations use this pool. These operations do not compete with regular commands for main-pool connections. The pool supplies burst capacity only:

  • The pool does not dial a connection before the connection is necessary.
  • An unused pool holds zero connections. The idle cost is zero.
  • If the pool is full, an operation immediately uses the main pool.

Set PipelinePoolSize: -1 to get the previous single-pool behavior.

🚀 Highlights

Full-Duplex Auto-Pipelining (Experimental)

Set AutoPipelineOptions.FullDuplex to enable the full-duplex mode of the automatic pipeliner. The default mode sends one batch for each round trip. The full-duplex mode is different: the engine holds one pipeline-pool connection, and a writer goroutine and a reader goroutine move the ordered command stream in the two directions at the same time. On a link with high latency, each command completes in approximately one round-trip time (RTT) on a single connection. A test on a 50 ms WAN profile measured ~389k operations/s at 52 ms p50. The half-duplex ordered path measured ~207k operations/s at 116 ms.

The mode operates on the two faces of a standalone Client: AutoPipeline() and AsyncAutoPipeline(). The mode also operates natively on a ClusterClient if the routing goes to masters only. A child engine for each master sends each command to the node that owns the command's slot. The engine obeys MOVED and ASK redirects and retryable replies (LOADING, READONLY, ...) through the usual cluster redirect procedure. If replica routing is set (ReadOnly, RouteByLatency, or RouteRandomly), the autopipeliner uses the half-duplex shard flushers, which obey the shard picker. Config().FullDuplex reports the mode that is in effect.

These options tune the mode:

  • FullDuplexWindow — the maximum number of commands in flight (backpressure).
  • FullDuplexIdleTimeout and FullDuplexMaxHold — control when the engine returns the held connection to the pool. The pool hooks then do the re-authentication and the maintenance-notification handoffs.
  • FullDuplexFastSubmit — an optional fast submit path for many producers on low-RTT links.

The full-duplex path supports blocking commands, Options.Limiter (one admission for each written batch), per-command hooks, OTel metrics, retry budgets (the client never sends a NoRetry command again after the command is on the wire), and seamless maintenance handoffs. The stream model causes these limits:

  • A hook can monitor a command. A hook cannot stop a command. A ProcessHook that returns without a call to next does not cancel the command on the full-duplex path. The command is already in the queue of the held connection, and the client sends it. Run policy hooks and kill-switch hooks on a plain client or on the half-duplex autopipeliner.
  • The order guarantee does not include diverted commands. The commands of one caller keep their order on the shared stream. The engine diverts some commands from the stream: blocking commands, connection-hostile commands, and managed HIMPORT commands. A diverted command has no order relation to the stream. On the async face, wait for the result of a diverted command before you submit a command that depends on it.
  • The fast path does not use the client-side cache. If CSC is enabled, a cacheable command on the full-duplex pipe does not read the cache and does not write to the cache. A subsequent change will correct this.
  • The engine records a duration metric for each reply. Some commands fail before they get to the reader: a lease failure, a limiter denial, retry exhaustion, or a close. These commands cause the error callback, but they do not cause an operation-duration sample.
rdb := redis.NewClient(&redis.Options{
	Addr: "localhost:6379",
	AutoPipelineOptions: &redis.AutoPipelineOptions{
		FullDuplex: true, // stream commands on one held connection, ~1 RTT each
	},
})
defer rdb.Close()

// Deferred face: calls return immediately; result accessors block until executed.
ap, err := rdb.AsyncAutoPipeline()
if err != nil {
	panic(err)
}
cmds := make([]*redis.StatusCmd, 0, 1000)
for i := 0; i < 1000; i++ {
	cmds = append(cmds, ap.Set(ctx, fmt.Sprintf("key:%d", i), i, 0))
}
for _, cmd := range cmds {
	if err := cmd.Err(); err != nil {
		// handle error
	}
}

// Or the blocking face — a drop-in Cmdable where each call blocks like a
// plain client while concurrent callers share the full-duplex pipe:
//	ap, err := rdb.AutoPipeline()
//	val, err := ap.Get(ctx, "key").Result()

The example/autopipeline directory contains a runnable tour of all the autopipeliner faces.

Two related defaults changed. AutoPipelineOptions.MaxBatchBytes now has a default of 128 KiB (before, the size had no limit). This default prevents a write/reply deadlock. It is not a throughput control. The buffers of the pipeline pool also have a default of 128 KiB.

Experimental: the auto-pipelining APIs can change in a minor release.

(#4002) by @ndyakov

Client-Side Caching: Refresh-on-Invalidate and Miss Coalescing

The release adds two functions to the experimental shared-tracking client-side cache. The functions have no effect unless CSC is enabled (#3989) by @ndyakov:

  • Refresh-on-invalidate (Options.ClientSideCacheRefreshOnInvalidate): the client reads recently-read keys again in the background immediately after their invalidation push arrives. The next reader does not pay the cache miss. The client collects invalidated hot keys in a short window and reads them again on a pipelined connection. ClientSideCacheRefreshRecencyWindow sets which entries count as recently read. ClientSideCacheInvalidationBatchWindow collects invalidation-driven cache deletes into background batches; without it, the connection reader applies each delete inline.
  • Miss coalescing (Options.ClientSideCacheCoalesceMisses): the client pipelines concurrent cache-miss reads onto one tracked connection. Each miss keeps the caller's own per-key command. The client does not rewrite the commands to MGET, so the function is safe on a cluster. The client writes each reply to the cache with the tracking generation of the connection. The server can thus invalidate each entry.

These options have the same requirement as the other CSC options: the built-in cache (ClientSideCacheConfig, or ClientSideCache set to a *LocalCache). The client ignores the options if a custom Cache implementation is set.

Better Distribution for Latency-Based Cluster Read Routing

RouteByLatency selects the node with the strictly minimum latency. The latency measurement has noise: the value is the mean of ten pings, and the client refreshes it at most each 10 s. Thus all clients can select the same node from a set of nodes that have almost equal latency. A production system showed this problem: the GET rates across a five-replica set in one availability zone had a 590x spread.

The new ClusterOptions.RouteByLatencyTolerance widens the selection to each node with a latency in the tolerance above the fastest node. The client distributes reads across these nodes with the round-robin procedure of the ShardPicker. A node in a different availability zone stays outside a sensible tolerance, so zone locality is kept. The default is zero, which keeps the strict-minimum behavior. The option is also on FailoverOptions, where it applies to clients from NewFailoverClusterClient. The plain NewFailoverClient does not support latency routing. (#3973) by @jozenstar

The same work corrected a routing defect. The client recorded the nearest healthy node only when that node was also the fastest node overall. A node that fails fast (a refused connection fails fast, so this is frequent) thus hid each healthy node, and the client sent reads to the node that failed. The client now records the healthy minimum separately. (#3994) by @jozenstar

✨ New Features

  • Full-duplex auto-pipelining: AutoPipelineOptions.FullDuplex, with FullDuplexWindow / FullDuplexIdleTimeout / FullDuplexMaxHold / FullDuplexFastSubmit. Available on standalone clients and cluster clients (#4002) by @ndyakov
  • Dedicated pipeline pool by default: PipelinePoolSize has a default of DefaultPipelinePoolSize (10) on each client. If the pool is full, an operation immediately uses the main pool. Set -1 to disable the pool (#4002, #3959) by @ndyakov
  • CSC refresh-on-invalidate and miss coalescing: Options.ClientSideCacheRefreshOnInvalidate (with ClientSideCacheRefreshRecencyWindow / ClientSideCacheInvalidationBatchWindow) and Options.ClientSideCacheCoalesceMisses (#3989) by @ndyakov
  • RouteByLatencyTolerance: distributes reads across nodes that have almost equal latency, on cluster clients and on NewFailoverClusterClient (#3973) by @jozenstar
  • AutoPipeliner.WaitClosed: blocks until the drain of the accepted commands completes, and returns the drain result. Use it in a wrapper that must not close shared pools while a flush is in progress (#3998) by @ndyakov
  • CMSInfo.CellSize: contains the cell-size field of CMS.INFO in Redis 8.12 (#4010) by @elena-kolevska

🐛 Bug Fixes

  • Cluster read routing: the client records the nearest healthy node separately from the overall minimum. A node that fails fast does not hide the healthy nodes (#3994) by @jozenstar
  • Probabilistic *.INFO forward compatibility: the parsers for BF.INFO / CF.INFO / CMS.INFO / TOPK.INFO / TDIGEST.INFO skip unknown fields and do not return an error. Redis 8.12 adds cell size to CMS.INFO (#4010) by @elena-kolevska
  • NewClient panic leak: a panic during construction (for example, a maintnotifications failure in ModeEnabled) does not leak the connection pools that already exist. A typed-nil pool cannot hide the initial panic (#4003) by @ndyakov. The same guards are applied to NewFailoverClient (#4002)
  • File-descriptor leak on rejected connections: Conn.Close does the socket teardown and the unsubscribe/CSC callbacks when the connection is already CLOSED. Before, init and auth failures collected open descriptors. The transport now closes exactly one time for each socket generation (fixes #3982) (#3985) by @ndyakov
  • Global logger races: atomics protect the global Logger and LogLevel. The call-site attribution is correct again (#3988) by @saddamr3e
  • Conn.onClose data race: the close hooks that init installs (onClose and onCscClose) are now atomic against a concurrent Close (#3966) by @saddamr3e
  • Reply-parser hardening: the reply parsers accept zero-length entry arrays (#3995) by @saddamr3e. FTHybridCmd reads the full RESP3 map reply and does not desynchronize the connection (#3956) by @saddamr3e
  • CLIENT INFO forward compatibility: the parser skips unknown client-flag characters. The full reply does not fail (#3977) by @ndyakov
  • GEOSEARCH duplicate args: the command does not send duplicate arguments (#3955) by @mehmettokgoz
  • MSetEX cluster routing: the constructor sets the first-key position. Typed calls thus go to the correct slot (#3984) by @shivamrustagi
  • Maintenance notifications: the client does not do the endpoint DNS detection when the mode is disabled (#3969) by @Phalanyx
  • Autopipeliner Close: a concurrent Close does not block (no re-entrant deadlock). WaitClosed supplies the drain result (#3998) by @ndyakov
  • Buffered-push log noise: the buffered-push-data notice in isHealthyConn shows only at the debug level. CSC invalidations do not fill the log (#3948) by @ndyakov
  • Sentinel teardown order: close hooks run in LIFO order. An autopipeliner drain thus completes before the Sentinel discovery stops. A closed failover client cannot create its Sentinel resources again from a late dial (#4002) by @ndyakov
  • Pipeline desync containment: if a pre-write push-notification drain fails, or if a command encoder panics, the client removes the connection. The client does not return a desynchronized connection to the pool. This applies to the shared Pipeline/TxPipeline path (#4002) by @ndyakov

⚡ Performance

  • Zero-copy scan: Scan gets zero-copy semantics, and the RESP reader does not do unnecessary data conversions (#3972) by @vlady-kotsev
  • Autopipeline straggler hold: the engine limits the hold on queued commands when the pipeline pool has a free connection. Uncached p95 decreased from 111 ms to 65 ms on a 50 ms link. Real-WAN uncached p99 decreased from 314 ms to 177 ms (#3962) by @ndyakov
  • Full-duplex allocations: a ring buffer holds the in-flight queue, and the blocking face uses a pool of batches. Allocations decreased from 770 B/op to 353 B/op at 2048 concurrent callers (#3970, part of #4002) by @ndyakov

🧪 Testing & Infrastructure

  • Fast skip gates: the tests do a TCP probe of each address before the Ping gate. This removes ~1.6 min of dial-retry waits in environments without the full stack (#4001) by @ndyakov
  • Redis Enterprise coverage: the autopipeline suites connect to the RE database and use the suite DB (#3976, #3975). The timing assertions scale to the measured RTT (#3978). The CLIENT INFO tracking-flag assertion does not run behind the RE proxy (#3981) by @ndyakov
  • Security policy: send vulnerability reports to the Redis VDP (#3949) by @ndyakov

👥 Contributors

We thank all the contributors who worked on this release!

@elena-kolevska, @jozenstar, @mehmettokgoz, @ndyakov, @Phalanyx, @saddamr3e, @shivamrustagi, @vlady-kotsev


Full Changelog: https://github.com/redis/go-redis/compare/v9.22.0...v9.23.0-beta.1