Skip to content

Commit 28f1e2a

Browse files
committed
Progress to get working Cassandra session
1 parent 7bfb16e commit 28f1e2a

5 files changed

Lines changed: 139 additions & 156 deletions

File tree

backend/cassandra/service.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package cassandra
2+
3+
import (
4+
"fmt"
5+
"github.com/Leo7Deng/ChatApp/models"
6+
"github.com/gocql/gocql"
7+
"github.com/google/uuid"
8+
)
9+
10+
func InsertMessage(session *gocql.Session, message models.Message) error {
11+
messageID := uuid.New().String()
12+
query := session.Query(`
13+
INSERT INTO chat_app.messages (circle_id, created_at, message_id, author_id, content)
14+
VALUES (?, ?, ?, ?, ?)`,
15+
message.CircleID, message.CreatedAt, messageID, message.AuthorID, message.Content)
16+
err := query.Exec()
17+
if err != nil {
18+
fmt.Println("Error inserting message into Cassandra:", err)
19+
return err
20+
}
21+
return nil
22+
}

backend/docker-compose.yml

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,24 @@ services:
6565
networks:
6666
- backend-net
6767
environment:
68+
CASSANDRA_START_RPC: "true"
69+
CASSANDRA_RPC_ADDRESS: "0.0.0.0"
70+
CASSANDRA_LISTEN_ADDRESS: "cassandra-node1"
71+
CASSANDRA_ENDPOINT_SNITCH: "GossipingPropertyFileSnitch"
6872
CASSANDRA_CLUSTER_NAME: "cassandra-cluster"
69-
CASSANDRA_SEEDS: "cassandra-node1,cassandra-node2,cassandra-node3"
70-
CASSANDRA_NUM_TOKENS: 128
73+
# CASSANDRA_SEEDS: "cassandra-node1,cassandra-node2"
74+
CASSANDRA_NUM_TOKENS: 8
75+
CASSANDRA_DC: "dc1"
7176
ports:
7277
- "9042:9042"
7378
volumes:
7479
- cassandra-data-node1:/var/lib/cassandra
80+
healthcheck:
81+
test: ["CMD-SHELL", "nodetool status"]
82+
interval: 2m
83+
timeout: 10s
84+
retries: 3
85+
start_period: 2m
7586

7687
cassandra-node2:
7788
image: cassandra:latest
@@ -80,13 +91,25 @@ services:
8091
networks:
8192
- backend-net
8293
depends_on:
83-
- cassandra-node1
94+
cassandra-node1:
95+
condition: service_healthy
8496
environment:
97+
CASSANDRA_START_RPC: "true"
98+
CASSANDRA_RPC_ADDRESS: "0.0.0.0"
99+
CASSANDRA_LISTEN_ADDRESS: "cassandra-node2"
100+
CASSANDRA_ENDPOINT_SNITCH: "GossipingPropertyFileSnitch"
85101
CASSANDRA_CLUSTER_NAME: "cassandra-cluster"
86102
CASSANDRA_SEEDS: "cassandra-node1"
87-
CASSANDRA_NUM_TOKENS: 128
103+
CASSANDRA_NUM_TOKENS: 8
104+
CASSANDRA_DC: "dc1"
88105
volumes:
89106
- cassandra-data-node2:/var/lib/cassandra
107+
healthcheck:
108+
test: ["CMD-SHELL", "nodetool status"]
109+
interval: 2m
110+
timeout: 10s
111+
retries: 3
112+
start_period: 2m
90113

91114
cassandra-node3:
92115
image: cassandra:latest
@@ -95,13 +118,25 @@ services:
95118
networks:
96119
- backend-net
97120
depends_on:
98-
- cassandra-node1
121+
cassandra-node2:
122+
condition: service_healthy
99123
environment:
124+
CASSANDRA_START_RPC: "true"
125+
CASSANDRA_RPC_ADDRESS: "0.0.0.0"
126+
CASSANDRA_LISTEN_ADDRESS: "cassandra-node3"
127+
CASSANDRA_ENDPOINT_SNITCH: "GossipingPropertyFileSnitch"
100128
CASSANDRA_CLUSTER_NAME: "cassandra-cluster"
101129
CASSANDRA_SEEDS: "cassandra-node1"
102-
CASSANDRA_NUM_TOKENS: 128
130+
CASSANDRA_NUM_TOKENS: 8
131+
CASSANDRA_DC: "dc1"
103132
volumes:
104133
- cassandra-data-node3:/var/lib/cassandra
134+
healthcheck:
135+
test: ["CMD-SHELL", "nodetool status"]
136+
interval: 2m
137+
timeout: 10s
138+
retries: 3
139+
start_period: 2m
105140
chat-app:
106141
build: .
107142
container_name: chat-app

backend/kafka/consumer.go

Lines changed: 72 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -6,159 +6,95 @@ import (
66
"errors"
77
"fmt"
88
"log"
9-
"os"
10-
"os/signal"
119
"sync"
12-
"syscall"
1310

1411
"github.com/IBM/sarama"
12+
"github.com/Leo7Deng/ChatApp/cassandra"
1513
"github.com/Leo7Deng/ChatApp/models"
1614
"github.com/Leo7Deng/ChatApp/websockets"
17-
// "github.com/gocql/gocql"
15+
"github.com/gocql/gocql"
1816
)
1917

2018
var hub *websockets.Hub
19+
var cassandraSession *gocql.Session
2120

22-
// StartConsumer continuously logs incoming messages
23-
func StartConsumer(ctx context.Context) {
24-
config := sarama.NewConfig()
25-
config.Consumer.Return.Errors = true
26-
27-
client, err := sarama.NewConsumer(brokers, config)
28-
if err != nil {
29-
log.Fatalf("Failed to start Kafka consumer: %v", err)
30-
}
31-
defer client.Close()
32-
33-
partitionConsumer, err := client.ConsumePartition(topic, 0, sarama.OffsetNewest)
34-
if err != nil {
35-
log.Fatalf("Failed to consume Kafka topic: %v", err)
36-
}
37-
defer partitionConsumer.Close()
38-
39-
fmt.Println("Kafka Consumer started...")
40-
41-
for {
42-
select {
43-
case msg := <-partitionConsumer.Messages():
44-
fmt.Printf("Kafka message: %s\n", string(msg.Value))
45-
var message models.Message
46-
err := json.Unmarshal(msg.Value, &message)
47-
if err != nil {
48-
fmt.Printf("Failed to unmarshal message: %v\n", err)
49-
}
21+
func WebsocketConsumer(ctx context.Context, websocketHub *websockets.Hub) {
22+
hub = websocketHub
23+
groupID := "websocket-group"
24+
handler := &WebsocketConsumerHandler{hub: websocketHub}
25+
runConsumer(ctx , groupID, handler)
26+
}
5027

51-
case <-ctx.Done():
52-
fmt.Println("Consumer shutting down...")
53-
return
54-
}
55-
}
28+
func CassandraConsumer(ctx context.Context, session *gocql.Session) {
29+
cassandraSession = session
30+
groupID := "cassandra-group"
31+
handler := &CassandraConsumerHandler{}
32+
runConsumer(ctx, groupID, handler)
5633
}
5734

58-
func WebsocketConsumer(ctx context.Context, websocketHub *websockets.Hub) {
59-
hub = websocketHub
60-
keepRunning := true
35+
func runConsumer(ctx context.Context, groupID string, handler sarama.ConsumerGroupHandler) {
6136
config := sarama.NewConfig()
6237
config.Consumer.Return.Errors = true
6338
config.Consumer.Group.Rebalance.GroupStrategies = []sarama.BalanceStrategy{sarama.NewBalanceStrategyRoundRobin()}
39+
config.Consumer.Offsets.Initial = sarama.OffsetNewest
6440

65-
66-
consumer := Consumer{
67-
ready: make(chan bool),
68-
}
69-
70-
ctx, cancel := context.WithCancel(context.Background())
71-
client, err := sarama.NewConsumerGroup(brokers, "websocket-group", config)
41+
client, err := sarama.NewConsumerGroup(brokers, groupID, config)
7242
if err != nil {
73-
log.Panicf("Error creating consumer group client: %v", err)
43+
log.Panicf("Error creating Kafka consumer group (%s): %v", groupID, err)
7444
}
45+
defer client.Close()
7546

76-
consumptionIsPaused := false
7747
wg := &sync.WaitGroup{}
7848
wg.Add(1)
49+
7950
go func() {
8051
defer wg.Done()
81-
for {
82-
err := client.Consume(ctx, []string{topic}, &consumer)
52+
for {
53+
err := client.Consume(ctx, []string{topic}, handler)
8354
if err != nil {
8455
if errors.Is(err, sarama.ErrClosedConsumerGroup) {
8556
return
8657
}
87-
log.Printf("Error from consumer: %v", err)
58+
log.Printf("Error from Kafka consumer (%s): %v", groupID, err)
8859
}
8960

9061
// If context is canceled, stop the consumer
9162
if ctx.Err() != nil {
9263
return
9364
}
94-
95-
// Reset readiness so we wait for the next session
96-
consumer.ready = make(chan bool)
9765
}
9866
}()
9967

100-
101-
<-consumer.ready // Await till the consumer has been set up
102-
log.Println("Sarama consumer up and running!...")
103-
104-
sigusr1 := make(chan os.Signal, 1)
105-
signal.Notify(sigusr1, syscall.SIGUSR1)
106-
107-
sigterm := make(chan os.Signal, 1)
108-
signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM)
109-
110-
for keepRunning {
111-
select {
112-
case <-ctx.Done():
113-
log.Println("terminating: context cancelled")
114-
keepRunning = false
115-
case <-sigterm:
116-
log.Println("terminating: via signal")
117-
keepRunning = false
118-
case <-sigusr1:
119-
toggleConsumptionFlow(client, &consumptionIsPaused)
120-
}
121-
}
122-
cancel()
68+
log.Printf("%s consumer started...", groupID)
69+
<-ctx.Done()
70+
log.Printf("%s consumer shutting down...", groupID)
12371
wg.Wait()
124-
if err = client.Close(); err != nil {
125-
log.Panicf("Error closing client: %v", err)
126-
}
12772
}
12873

129-
func toggleConsumptionFlow(client sarama.ConsumerGroup, isPaused *bool) {
130-
if *isPaused {
131-
client.ResumeAll()
132-
log.Println("Resuming consumption")
133-
} else {
134-
client.PauseAll()
135-
log.Println("Pausing consumption")
136-
}
13774

138-
*isPaused = !*isPaused
139-
}
140-
141-
// Consumer represents a Sarama consumer group consumer
142-
type Consumer struct {
143-
ready chan bool
144-
}
75+
type WebsocketConsumerHandler struct {hub *websockets.Hub}
76+
type CassandraConsumerHandler struct {}
14577

14678
// Setup is run at the beginning of a new session, before ConsumeClaim
147-
func (consumer *Consumer) Setup(sarama.ConsumerGroupSession) error {
148-
// Mark the consumer as ready
149-
close(consumer.ready)
79+
func (consumer *WebsocketConsumerHandler) Setup(sarama.ConsumerGroupSession) error {
80+
return nil
81+
}
82+
func (consumer *CassandraConsumerHandler) Setup(sarama.ConsumerGroupSession) error {
15083
return nil
15184
}
15285

15386
// Cleanup is run at the end of a session, once all ConsumeClaim goroutines have exited
154-
func (consumer *Consumer) Cleanup(sarama.ConsumerGroupSession) error {
87+
func (consumer *WebsocketConsumerHandler) Cleanup(sarama.ConsumerGroupSession) error {
88+
return nil
89+
}
90+
func (consumer *CassandraConsumerHandler) Cleanup(sarama.ConsumerGroupSession) error {
15591
return nil
15692
}
15793

15894
// ConsumeClaim must start a consumer loop of ConsumerGroupClaim's Messages().
15995
// Once the Messages() channel is closed, the Handler must finish its processing
16096
// loop and exit.
161-
func (consumer *Consumer) ConsumeClaim(session sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error {
97+
func (consumer *WebsocketConsumerHandler) ConsumeClaim(session sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error {
16298
// NOTE:
16399
// Do not move the code below to a goroutine.
164100
// The `ConsumeClaim` itself is called within a goroutine, see:
@@ -171,7 +107,7 @@ func (consumer *Consumer) ConsumeClaim(session sarama.ConsumerGroupSession, clai
171107
return nil
172108
}
173109
partition, offset := message.Partition, message.Offset
174-
log.Printf("Kafka websocket consumer: %s | Partition: %d | Offset: %d\n", message.Value, partition, offset)
110+
log.Printf("Kafka consumer: %s | Partition: %d | Offset: %d\n", message.Value, partition, offset)
175111
var websocketMessage models.WebsocketMessage
176112
err := json.Unmarshal(message.Value, &websocketMessage)
177113
if err != nil {
@@ -189,46 +125,38 @@ func (consumer *Consumer) ConsumeClaim(session sarama.ConsumerGroupSession, clai
189125
}
190126
}
191127

128+
func (consumer *CassandraConsumerHandler) ConsumeClaim(session sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error {
129+
for {
130+
select {
131+
case message, ok := <-claim.Messages():
132+
if !ok {
133+
log.Printf("message channel was closed")
134+
return nil
135+
}
136+
partition, offset := message.Partition, message.Offset
137+
var websocketMessage models.WebsocketMessage
138+
err := json.Unmarshal(message.Value, &websocketMessage)
139+
if err != nil {
140+
fmt.Printf("Failed to unmarshal message: %v\n", err)
141+
}
142+
log.Printf("Cassandra consumer: %s | Partition: %d | Offset: %d\n", message.Value, partition, offset)
143+
insertMessage := models.Message{
144+
CircleID: websocketMessage.Message.CircleID,
145+
AuthorID: websocketMessage.Message.AuthorID,
146+
Content: websocketMessage.Message.Content,
147+
CreatedAt: websocketMessage.Message.CreatedAt,
148+
}
149+
err = cassandra.InsertMessage(cassandraSession, insertMessage)
192150

193-
194-
195-
196-
197-
198-
199-
200-
201-
202-
203-
204-
205-
// //////////////////
206-
// group, err := sarama.NewConsumerGroup(brokers, "websocket-group", config)
207-
// if err != nil {
208-
// log.Fatalf("Failed to start Kafka consumer group: %v", err)
209-
// }
210-
// defer group.Close()
211-
212-
// fmt.Println("Kafka Consumer started...")
213-
214-
// for {
215-
// select {
216-
// case msg := <-partitionConsumer.Messages():
217-
// var websocketMessage models.WebsocketMessage
218-
// fmt.Printf("Kafka consumer viewed: %s\n", string(msg.Value))
219-
// err := json.Unmarshal(msg.Value, &websocketMessage)
220-
// if err != nil {
221-
// fmt.Printf("Failed to unmarshal message: %v\n", err)
222-
// }
223-
// websocketMessage.Origin = "server"
224-
// hub.SendWebsocketMessage(websocketMessage)
225-
// case <-ctx.Done():
226-
// fmt.Println("Consumer shutting down...")
227-
// return
228-
// }
229-
// }
230-
// }
231-
232-
// func CassandraConsumer(ctx context.Context, cassandraSession *gocql.Session) {
233-
234-
// }
151+
// Handle error on unprocessed message insert into Cassandra
152+
if err != nil {
153+
fmt.Printf("Failed to insert message: %v\n", err)
154+
} else {
155+
log.Printf("Message inserted into Cassandra: %v\n", insertMessage)
156+
session.MarkMessage(message, "")
157+
}
158+
case <-session.Context().Done():
159+
return nil
160+
}
161+
}
162+
}

0 commit comments

Comments
 (0)