Skip to content

Add Notifier for running several instances on one storage - #167

Merged
mattn merged 1 commit into
fiatjaf:masterfrom
mattn:add-notifier
Sep 14, 2026
Merged

mattn merged 1 commit into
fiatjaf:masterfrom
mattn:add-notifier

Conversation

@mattn

@mattn mattn commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Problem

Live subscriptions are tracked in memory (Server.listeners), so when several relay processes share one database, an event published to instance a is only pushed to the clients connected to a. Clients subscribed on b or c never see it until they open a new REQ.

Change

Adds an optional Notifier interface:

type Notifier interface {
	Notify(context.Context, *nostr.Event) error
	Notifications(context.Context) (<-chan *nostr.Event, error)
}
  • Notify is called by AddEvent for every accepted event, after it has been saved. Ephemeral events are passed too, since they are never saved and would otherwise be invisible to a storage-level implementation.
  • Notifications is subscribed by NewServer; whatever arrives is delivered through the usual notifyListeners, so REQ filters apply as before.
  • When a Notifier is present, AddEvent no longer calls notifyListeners directly. Events reach local subscribers only through Notifications, giving a single delivery path with no duplicates.
  • It is resolved on the Relay first, then on the eventstore.Store, so a backend with native change notifications (e.g. PostgreSQL LISTEN/NOTIFY) can provide one later, while any backend can be combined with a transport of the user's choice today.
  • If Notifications fails, NewServer returns the error; the subscription context is cancelled on Shutdown.

Relays that don't implement Notifier behave exactly as before. Injector is untouched.

Example

examples/multi-instance runs several processes on one SQLite file (WAL mode), kept in sync over Redis pub/sub. Adds github.com/redis/go-redis/v9 to the module for the example.

Tests

notifier_test.go wires two servers to one store through an in-memory bus and checks that an event published to one is delivered to subscribers on both, exactly once each; that a store-level Notifier works; that ephemeral events are notified; and that a failing Notifications fails NewServer.

Also verified end to end with the example: two processes + Redis, nak event to one, nak req --stream on the other receives it.

Live subscriptions are tracked in memory, so an event published to one
relay process was only pushed to the clients of that process. Notifier
propagates accepted events between instances sharing a storage:
Notify is called for every accepted event (ephemeral ones included) and
Notifications delivers the events accepted by any instance.

When a Notifier is present, AddEvent hands events over to Notify and
delivery to local subscribers happens only through Notifications, so
there is a single path and no duplicates. It is looked up on the Relay
first and on the Store second, so a backend with native change
notifications can provide one while any other backend can be combined
with a transport of the user's choice.

Add an example running several processes on one SQLite file, kept in
sync over Redis pub/sub.
@mattn
mattn merged commit 995f706 into fiatjaf:master Sep 14, 2026
1 check passed
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