Skip to content

Add xsync.ChangeObservable() and bump minimum Go version to v1.27 - #187

Merged
jmalloc merged 2 commits into
mainfrom
xsync-store-if-changed
Nov 1, 2026
Merged

jmalloc merged 2 commits into
mainfrom
xsync-store-if-changed

Conversation

@jmalloc

@jmalloc jmalloc commented Oct 31, 2026 •

Copy link
Copy Markdown
Member

Summary

Adds xsync.ChangeObservable(), which updates an xsync.Observable only if the new value differs from its current value. Readers are woken only when something actually changed.

func ChangeObservable[T comparable](o *Observable[T], v T) bool

It reports whether the value was changed. The comparison and the swap happen in the same compare-and-swap loop that Store uses, so it's atomic with any number of concurrent writers: two writers can't both see the old value and both report a change. "Change" carries the "only if different" sense on its own, so the name doesn't need an "if changed" suffix.

It's a package-level function rather than a method because it needs T to be comparable, and Observable[T any] doesn't require that. Go 1.27's method type parameters can't add a constraint to the receiver's T or tie a method's own type parameter to it, so a method would have to fall back to a runtime type assertion.

This PR also bumps the minimum Go version to v1.27.

Motivation

runkit's projection consumer stores whether this node is elected to consume each event stream, and each stream's worker restarts consumption when that value changes. Storing an unchanged value wakes the worker for nothing. Comparing before storing outside xsync isn't atomic, so this belongs here.

Testing

x/xsync has tests for changing the value, skipping an unchanged one, treating the zero value as the initial value, and concurrent writers. The concurrent test checks that the final boolean matches the parity of the calls that reported a change. It fails against a non-atomic load-compare-store.

🤖 Generated with Claude Code

@jmalloc jmalloc changed the title Add xsync.StoreIfChanged() and bump minimum Go version to v1.27 Add xsync.ChangeObservable() and bump minimum Go version to v1.27 Oct 31, 2026
jmalloc and others added 2 commits November 2, 2026 09:53
`StoreIfChanged()` updates an `Observable` only if the new value differs from
its current value, so that readers are not woken when nothing has changed. The
comparison and the swap are a single atomic step, so it is safe with multiple
writers.

It is a package-level function rather than a method because it requires the
value type to be comparable, which `Observable` itself does not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Change" carries the "only if different" sense on its own, and the name now
says which type it operates on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jmalloc
jmalloc force-pushed the xsync-store-if-changed branch from d12b170 to 9295094 Compare November 1, 2026 23:53
@jmalloc
jmalloc enabled auto-merge November 1, 2026 23:54
@jmalloc
jmalloc merged commit 6d8e998 into main Nov 1, 2026
11 checks passed
@jmalloc
jmalloc deleted the xsync-store-if-changed branch November 1, 2026 23:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant