An experimental pub/sub client and server project.

give each topic its own store

+29 -43
+1 -1
example/server/main.go
··· 11 11 ) 12 12 13 13 func main() { 14 - srv, err := server.New(":3000", time.Second, time.Second*2, server.NewMemoryStore()) 14 + srv, err := server.New(":3000", time.Second, time.Second*2) 15 15 if err != nil { 16 16 log.Fatal(err) 17 17 }
+1 -12
pubsub/subscriber_test.go
··· 18 18 topicB = "topic b" 19 19 ) 20 20 21 - type fakeStore struct { 22 - } 23 - 24 - func (f *fakeStore) Write(msg server.MessageToSend) error { 25 - return nil 26 - } 27 - func (f *fakeStore) ReadFrom(offset int, handleFunc func(msgs []server.MessageToSend)) error { 28 - return nil 29 - } 30 - 31 21 func createServer(t *testing.T) { 32 - fs := &fakeStore{} 33 - server, err := server.New(serverAddr, time.Millisecond*100, time.Millisecond*100, fs) 22 + server, err := server.New(serverAddr, time.Millisecond*100, time.Millisecond*100) 34 23 require.NoError(t, err) 35 24 36 25 t.Cleanup(func() {
+2 -5
server/message_store.go
··· 28 28 return nil 29 29 } 30 30 31 - func (m *MemoryStore) ReadFrom(offset int, handleFunc func(msgs []MessageToSend)) error { 31 + func (m *MemoryStore) ReadFrom(offset int, handleFunc func(msg MessageToSend)) error { 32 32 if offset < 0 || offset > m.offset { 33 33 return fmt.Errorf("invalid offset provided") 34 34 } ··· 36 36 m.mu.Lock() 37 37 defer m.mu.Unlock() 38 38 39 - msgs := make([]MessageToSend, 0, len(m.msgs)) 40 39 for i := offset; i < len(m.msgs); i++ { 41 - msgs = append(msgs, m.msgs[i]) 40 + handleFunc(m.msgs[i]) 42 41 } 43 - 44 - handleFunc(msgs) 45 42 46 43 return nil 47 44 }
+9 -12
server/server.go
··· 77 77 78 78 type Store interface { 79 79 Write(msg MessageToSend) error 80 - ReadFrom(offset int, handleFunc func(msgs []MessageToSend)) error 80 + ReadFrom(offset int, handleFunc func(msg MessageToSend)) error 81 81 } 82 82 83 83 // Server accepts subscribe and publish connections and passes messages around ··· 90 90 91 91 ackDelay time.Duration 92 92 ackTimeout time.Duration 93 - 94 - messageStore Store 95 93 } 96 94 97 95 // New creates and starts a new server 98 - func New(Addr string, ackDelay, ackTimeout time.Duration, messageStore Store) (*Server, error) { 96 + func New(Addr string, ackDelay, ackTimeout time.Duration) (*Server, error) { 99 97 lis, err := net.Listen("tcp", Addr) 100 98 if err != nil { 101 99 return nil, fmt.Errorf("failed to listen: %w", err) 102 100 } 103 101 104 102 srv := &Server{ 105 - lis: lis, 106 - topics: map[string]*topic{}, 107 - ackDelay: ackDelay, 108 - ackTimeout: ackTimeout, 109 - messageStore: messageStore, 103 + lis: lis, 104 + topics: map[string]*topic{}, 105 + ackDelay: ackDelay, 106 + ackTimeout: ackTimeout, 110 107 } 111 108 112 109 go srv.start() ··· 375 372 376 373 topic := s.getTopic(message.topic) 377 374 if topic == nil { 378 - topic = newTopic(message.topic, s.messageStore) 375 + topic = newTopic(message.topic) 379 376 s.topics[message.topic] = topic 380 377 } 381 378 ··· 406 403 407 404 t, ok := s.topics[topicName] 408 405 if !ok { 409 - t = newTopic(topicName, s.messageStore) 406 + t = newTopic(topicName) 410 407 } 411 408 412 - t.subscriptions[peer.Addr()] = newSubscriber(peer, topicName, s.ackDelay, s.ackTimeout, s.messageStore, startAt) 409 + t.subscriptions[peer.Addr()] = newSubscriber(peer, topicName, s.ackDelay, s.ackTimeout, t.messageStore, startAt) 413 410 414 411 s.topics[topicName] = t 415 412 }
+12 -5
server/server_test.go
··· 24 24 ) 25 25 26 26 func createServer(t *testing.T) *Server { 27 - store := NewMemoryStore() 28 - srv, err := New(serverAddr, ackDelay, ackTimeout, store) 27 + srv, err := New(serverAddr, ackDelay, ackTimeout) 29 28 require.NoError(t, err) 30 29 31 30 t.Cleanup(func() { ··· 40 39 srv.topics[topicName] = &topic{ 41 40 name: topicName, 42 41 subscriptions: make(map[net.Addr]*subscriber), 42 + messageStore: NewMemoryStore(), 43 43 } 44 44 45 45 return srv ··· 516 516 messages = append(messages, fmt.Sprintf("message %d", i)) 517 517 } 518 518 519 - // send messages first 520 519 topic := fmt.Sprintf("topic:%s", topicA) 521 520 522 - // send multiple messages 523 521 for _, msg := range messages { 524 522 sendMessage(t, publisherConn, topic, []byte(msg)) 525 523 } 524 + 525 + // send some messages for topic B as well 526 + sendMessage(t, publisherConn, fmt.Sprintf("topic:%s", topicB), []byte("topic b message 1")) 527 + sendMessage(t, publisherConn, fmt.Sprintf("topic:%s", topicB), []byte("topic b message 2")) 528 + sendMessage(t, publisherConn, fmt.Sprintf("topic:%s", topicB), []byte("topic b message 3")) 526 529 527 530 subscriberConn := createConnectionAndSubscribe(t, []string{topicA}, From, 0) 528 531 results := make([]string, 0, len(messages)) ··· 547 550 messages = append(messages, fmt.Sprintf("message %d", i)) 548 551 } 549 552 550 - // send messages first 551 553 topic := fmt.Sprintf("topic:%s", topicA) 552 554 553 555 // send multiple messages 554 556 for _, msg := range messages { 555 557 sendMessage(t, publisherConn, topic, []byte(msg)) 556 558 } 559 + 560 + // send some messages for topic B as well 561 + sendMessage(t, publisherConn, fmt.Sprintf("topic:%s", topicB), []byte("topic b message 1")) 562 + sendMessage(t, publisherConn, fmt.Sprintf("topic:%s", topicB), []byte("topic b message 2")) 563 + sendMessage(t, publisherConn, fmt.Sprintf("topic:%s", topicB), []byte("topic b message 3")) 557 564 558 565 subscriberConn := createConnectionAndSubscribe(t, []string{topicA}, From, 3) 559 566
+2 -7
server/subscriber.go
··· 44 44 offset := startAt 45 45 46 46 go func() { 47 - // here we need to replay all messages from the store for the topic. 48 - err := messageStore.ReadFrom(offset, func(msgs []MessageToSend) { 49 - // go func() { 50 - for _, msg := range msgs { 51 - s.messages <- newMessage(msg.data) 52 - } 53 - // }() 47 + err := messageStore.ReadFrom(offset, func(msg MessageToSend) { 48 + s.messages <- newMessage(msg.data) 54 49 }) 55 50 if err != nil { 56 51 slog.Error("failed to replay messages from offset", "error", err, "offset", offset)
+2 -1
server/topic.go
··· 13 13 messageStore Store 14 14 } 15 15 16 - func newTopic(name string, messageStore Store) *topic { 16 + func newTopic(name string) *topic { 17 + messageStore := NewMemoryStore() 17 18 return &topic{ 18 19 name: name, 19 20 subscriptions: make(map[net.Addr]*subscriber),