···11+package server
22+33+import (
44+ "sync"
55+)
66+77+// Memory store allows messages to be stored in memory
88+type MemoryStore struct {
99+ mu sync.Mutex
1010+ msgs map[int]message
1111+ nextOffset int
1212+}
1313+1414+// New memory store initializes a new in memory store
1515+func NewMemoryStore() *MemoryStore {
1616+ return &MemoryStore{
1717+ msgs: make(map[int]message),
1818+ }
1919+}
2020+2121+// Write will write the provided message to the in memory store
2222+func (m *MemoryStore) Write(msg message) error {
2323+ m.mu.Lock()
2424+ defer m.mu.Unlock()
2525+2626+ m.msgs[m.nextOffset] = msg
2727+2828+ m.nextOffset++
2929+3030+ return nil
3131+}
3232+3333+// ReadFrom will read messages from (and including) the provided offset and pass them to the provided handler
3434+func (m *MemoryStore) ReadFrom(offset int, handleFunc func(msg message)) {
3535+ if offset < 0 || offset >= m.nextOffset {
3636+ return
3737+ }
3838+3939+ m.mu.Lock()
4040+ defer m.mu.Unlock()
4141+4242+ for i := offset; i < len(m.msgs); i++ {
4343+ handleFunc(m.msgs[i])
4444+ }
4545+}
+59-31
server/server.go
···3131type Status uint16
32323333const (
3434- Subscribed = 1
3535- Unsubscribed = 2
3636- Error = 3
3434+ Subscribed Status = 1
3535+ Unsubscribed Status = 2
3636+ Error Status = 3
3737)
38383939func (s Status) String() string {
···48484949 return ""
5050}
5151+5252+// StartAtType represents where the subcriber wishes to start subscribing to a topic from
5353+type StartAtType uint16
5454+5555+const (
5656+ Beginning StartAtType = 0
5757+ Current StartAtType = 1
5858+ From StartAtType = 2
5959+)
51605261// Server accepts subscribe and publish connections and passes messages around
5362type Server struct {
···198207 return nil
199208 }
200209201201- s.subscribeToTopics(peer, topics)
210210+ var startAtType StartAtType
211211+ err = binary.Read(conn, binary.BigEndian, &startAtType)
212212+ if err != nil {
213213+ slog.Error(err.Error(), "peer", peer.Addr())
214214+ writeStatus(Error, "invalid start at type provided", conn)
215215+ return nil
216216+ }
217217+ var startAt int
218218+ switch startAtType {
219219+ case From:
220220+ var s uint16
221221+ err = binary.Read(conn, binary.BigEndian, &s)
222222+ if err != nil {
223223+ slog.Error(err.Error(), "peer", peer.Addr())
224224+ writeStatus(Error, "invalid start at value provided", conn)
225225+ return nil
226226+ }
227227+ startAt = int(s)
228228+ case Beginning:
229229+ startAt = 0
230230+ case Current:
231231+ startAt = -1
232232+ default:
233233+ slog.Error("invalid start up type provided", "start up type", startAtType)
234234+ writeStatus(Error, "invalid start up type provided", conn)
235235+ return nil
236236+ }
237237+238238+ s.subscribeToTopics(peer, topics, startAt)
202239 writeStatus(Subscribed, "", conn)
203240204241 return nil
···247284 _ = peer.RunConnOperation(op)
248285}
249286250250-type messageToSend struct {
251251- topic string
252252- data []byte
253253-}
254254-255287func (s *Server) handlePublish(peer *peer.Peer) {
256288 slog.Info("handling publisher", "peer", peer.Addr())
257289 for {
258258- var message *messageToSend
259259-260290 op := func(conn net.Conn) error {
261291 dataLen, err := dataLength(conn)
262292 if err != nil {
···304334 return nil
305335 }
306336307307- message = &messageToSend{
308308- topic: topicStr,
309309- data: dataBuf,
337337+ topic := s.getTopic(topicStr)
338338+ if topic == nil {
339339+ topic = newTopic(topicStr)
340340+ s.topics[topicStr] = topic
341341+ }
342342+343343+ message := newMessage(dataBuf)
344344+345345+ err = topic.sendMessageToSubscribers(message)
346346+ if err != nil {
347347+ slog.Error("failed to send message to subscribers", "error", err, "peer", peer.Addr())
348348+ writeStatus(Error, "failed to send message to subscribers", conn)
349349+ return nil
310350 }
351351+311352 return nil
312353 }
313354314355 _ = peer.RunConnOperation(op)
315315-316316- if message == nil {
317317- continue
318318- }
319319-320320- // sending messages to the subscribers can be done async because the publisher doesn't need to wait for
321321- // subscribers to be sent the message
322322- go func() {
323323- topic := s.getTopic(message.topic)
324324- if topic != nil {
325325- topic.sendMessageToSubscribers(message.data)
326326- }
327327- }()
328356 }
329357}
330358331331-func (s *Server) subscribeToTopics(peer *peer.Peer, topics []string) {
359359+func (s *Server) subscribeToTopics(peer *peer.Peer, topics []string, startAt int) {
332360 slog.Info("subscribing peer to topics", "topics", topics, "peer", peer.Addr())
333361 for _, topic := range topics {
334334- s.addSubsciberToTopic(topic, peer)
362362+ s.addSubsciberToTopic(topic, peer, startAt)
335363 }
336364}
337365338338-func (s *Server) addSubsciberToTopic(topicName string, peer *peer.Peer) {
366366+func (s *Server) addSubsciberToTopic(topicName string, peer *peer.Peer, startAt int) {
339367 s.mu.Lock()
340368 defer s.mu.Unlock()
341369···344372 t = newTopic(topicName)
345373 }
346374347347- t.subscriptions[peer.Addr()] = newSubscriber(peer, topicName, s.ackDelay, s.ackTimeout)
375375+ t.subscriptions[peer.Addr()] = newSubscriber(peer, t, s.ackDelay, s.ackTimeout, startAt)
348376349377 s.topics[topicName] = t
350378}