···7NOTES:
8- this should run as close to your infrastructure as possible. you then
9 would connect to `ws://localhost:6969/subscribe`
00001011```
12go build
···7NOTES:
8- this should run as close to your infrastructure as possible. you then
9 would connect to `ws://localhost:6969/subscribe`
10+- no cursor support. since there will be multiple jetstream upstreams that
11+ run at separate cursor timelines, it would be pretty hard to rewrite cursors
12+ in such a way that everything works. if your application relies on cursors,
13+ then it's probably best for your application to deal with multi-upstream support.
1415```
16go build
+23-2
main.go
···7 "os"
8 "strings"
9 "sync"
010 "time"
1112 "github.com/gorilla/websocket"
···26type Broadcaster struct {
27 listeners []chan []byte
28 mu sync.Mutex
029}
3031// Subscribe returns a new channel that will receive Jetstream events
···84 }
8586 start := time.Now()
0087 resp, err := client.Get(url)
88 if err != nil {
89 return 0, err
···99 },
100}
1010000000000000102// handleSubscribe upgrades HTTP connection to websocket and streams events
103func handleSubscribe(broadcaster *Broadcaster) http.HandlerFunc {
104 return func(w http.ResponseWriter, r *http.Request) {
···130131// connectToUpstream maintains a connection to the upstream websocket and broadcasts messages
132func connectToUpstream(pool []string, broadcaster *Broadcaster) {
133- backoff := time.Second
134- maxBackoff := time.Minute
135 var currentUpstream string
136137 for {
···157 conn, _, err := websocket.DefaultDialer.Dial(currentUpstream+"/subscribe", nil)
158 if err != nil {
159 slog.Error("Failed to connect to upstream", slog.String("url", currentUpstream), slog.Any("error", err))
0160 time.Sleep(backoff)
161 backoff *= 2
162 if backoff > maxBackoff {
···166 }
167168 slog.Info("Connected to upstream", slog.String("url", currentUpstream))
0169 backoff = time.Second // Reset backoff on successful connection
170171 // Read messages from upstream and broadcast them
···173 messageType, message, err := conn.ReadMessage()
174 if err != nil {
175 slog.Error("Error reading from upstream", slog.Any("error", err))
0176 conn.Close()
177 break
178 }
···271 go connectToUpstream(pool, broadcaster)
272273 // Setup HTTP server
0274 http.HandleFunc("/subscribe", handleSubscribe(broadcaster))
275276 slog.Info("Starting proxy server", slog.String("bind", bindAddr))
···7 "os"
8 "strings"
9 "sync"
10+ "sync/atomic"
11 "time"
1213 "github.com/gorilla/websocket"
···27type Broadcaster struct {
28 listeners []chan []byte
29 mu sync.Mutex
30+ connected atomic.Bool
31}
3233// Subscribe returns a new channel that will receive Jetstream events
···86 }
8788 start := time.Now()
89+ // jetstream instances return the "Welcome to jetstream!" banner on / which
90+ // should be useful enough for latency
91 resp, err := client.Get(url)
92 if err != nil {
93 return 0, err
···103 },
104}
105106+// handleHealth returns 200 if connected to upstream
107+func handleHealth(broadcaster *Broadcaster) http.HandlerFunc {
108+ return func(w http.ResponseWriter, r *http.Request) {
109+ if broadcaster.connected.Load() {
110+ w.WriteHeader(http.StatusOK)
111+ w.Write([]byte("Welcome to jetstream!"))
112+ } else {
113+ w.WriteHeader(http.StatusServiceUnavailable)
114+ w.Write([]byte("Not connected to upstream"))
115+ }
116+ }
117+}
118+119// handleSubscribe upgrades HTTP connection to websocket and streams events
120func handleSubscribe(broadcaster *Broadcaster) http.HandlerFunc {
121 return func(w http.ResponseWriter, r *http.Request) {
···147148// connectToUpstream maintains a connection to the upstream websocket and broadcasts messages
149func connectToUpstream(pool []string, broadcaster *Broadcaster) {
150+ backoff := 50 * time.Millisecond
151+ maxBackoff := 20 * time.Second
152 var currentUpstream string
153154 for {
···174 conn, _, err := websocket.DefaultDialer.Dial(currentUpstream+"/subscribe", nil)
175 if err != nil {
176 slog.Error("Failed to connect to upstream", slog.String("url", currentUpstream), slog.Any("error", err))
177+ broadcaster.connected.Store(false)
178 time.Sleep(backoff)
179 backoff *= 2
180 if backoff > maxBackoff {
···184 }
185186 slog.Info("Connected to upstream", slog.String("url", currentUpstream))
187+ broadcaster.connected.Store(true)
188 backoff = time.Second // Reset backoff on successful connection
189190 // Read messages from upstream and broadcast them
···192 messageType, message, err := conn.ReadMessage()
193 if err != nil {
194 slog.Error("Error reading from upstream", slog.Any("error", err))
195+ broadcaster.connected.Store(false)
196 conn.Close()
197 break
198 }
···291 go connectToUpstream(pool, broadcaster)
292293 // Setup HTTP server
294+ http.HandleFunc("/", handleHealth(broadcaster))
295 http.HandleFunc("/subscribe", handleSubscribe(broadcaster))
296297 slog.Info("Starting proxy server", slog.String("bind", bindAddr))