Skip to content

Conversation

@brenoamin
Copy link
Contributor

Overview

This project implements a concurrent chat server in Go using goroutines and channels.
It supports multiple clients, message broadcasting, private messaging, and safe connection
management, fully aligned with the challenge requirements.

Features

  • Multiple clients can connect concurrently
  • Unique username enforcement
  • Broadcast messages to all connected clients
  • Private messaging between specific clients
  • Graceful client disconnection
  • Thread-safe operations (mutex + channels)
  • Ordered message delivery
  • Robust error handling
  • Fully covered by automated tests

Architecture

  • ChatServer
    • Manages connected clients
    • Routes broadcast and private messages
    • Ensures thread safety
  • Client
    • Has a unique username
    • Uses channels for incoming/outgoing messages
    • Supports blocking receive operations

Concurrency Model

  • Goroutines handle concurrent message sending and receiving
  • Channels manage communication between server and clients
  • Mutex protects shared server state to prevent race conditions

Testing

The implementation is validated with tests covering:

  • Client connection and disconnection
  • Duplicate username handling
  • Broadcast messaging
  • Private messaging
  • Error handling for invalid recipients
  • Concurrent message flow without deadlocks or races

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 13, 2025

Walkthrough

The PR adds two Go solutions for coding challenges: a simple program computing the sum of two integers for challenge-1, and a concurrent chat server with client connection management, broadcast messaging, and private messaging for challenge-8.

Changes

Cohort / File(s) Summary
Challenge 1 Sum Solution
challenge-1/submissions/brenoamin/solution-template.go
Implements Sum(a int, b int) int function and a main program that reads two space-separated integers from stdin and outputs their sum.
Challenge 8 Chat Server
challenge-8/submissions/brenoamin/solution-template.go
Introduces Client struct with connection state and message channels, and ChatServer struct with methods for client lifecycle management (Connect, Disconnect), message routing (Broadcast, PrivateMessage), and concurrency controls via mutexes. Defines exported error variables for validation failures.

Sequence Diagram

sequenceDiagram
    actor User1 as User 1
    participant CS as ChatServer
    actor User2 as User 2

    User1->>CS: Connect(username="alice")
    CS->>CS: Validate username uniqueness
    CS-->>User1: Return Client & Outgoing channel

    User2->>CS: Connect(username="bob")
    CS->>CS: Validate username uniqueness
    CS-->>User2: Return Client & Outgoing channel

    User1->>CS: Broadcast("hello everyone")
    CS->>CS: Format "alice: hello everyone"
    CS->>User2: Send to User2's Outgoing channel
    CS-->>User1: Log sent to 1 client

    User2->>CS: PrivateMessage(recipient="alice", message="hi alice")
    CS->>CS: Validate sender connected
    CS->>CS: Locate recipient "alice"
    CS->>User1: Send "bob: hi alice"
    CS-->>User2: Return success

    User1->>CS: Disconnect(client)
    CS->>CS: Remove from clients map
    CS->>CS: Close Outgoing channel
    CS-->>User1: Logged disconnection
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Areas requiring extra attention:

  • Challenge-8 concurrency controls: Review mutex usage in ChatServer and Client for proper synchronization of shared state (clients map, Disconnected flag)
  • Challenge-8 error handling: Verify all error paths (e.g., ErrSenderDisconnected, ErrRecipientNotFound, ErrRecipientDisconnected) are properly checked and returned by callers
  • Challenge-8 message channel lifecycle: Ensure the Outgoing channel is safely managed across Connect, Send, Receive, and Disconnect operations to avoid panics or deadlocks

Possibly related PRs

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: implementing a Chat Server with Channels for Challenge 8 in Go, matching the core objective of the PR.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, covering the chat server implementation, features, architecture, concurrency model, and testing approach.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
challenge-8/submissions/brenoamin/solution-template.go (2)

8-8: Unused import: net package is imported but never used.

The Conn field is always set to nil in Connect. Consider removing the import and the Conn field if network connections are not being used.

 import (
 	"errors"
 	"fmt"
 	"log"
-	"net"
 	"strings"
 	"sync"
-	// Add any other necessary imports
 )

And in the Client struct:

 type Client struct {
 	Username     string
-	Conn         net.Conn
 	Outgoing     chan string
 	Disconnected bool
 	mu           sync.Mutex
 }

48-56: Consider removing unused channel fields.

The broadcast, join, and leave channels are declared but never used. The current implementation routes messages via direct method calls rather than a channel-based event loop. Removing these fields would reduce confusion.

 type ChatServer struct {
-	// Hint: clients map, mutex
-	clients   map[*Client]bool
-	broadcast chan string
-	join      chan *Client
-	leave     chan *Client
-	mu        sync.Mutex
+	clients map[*Client]bool
+	mu      sync.Mutex
 }

And update NewChatServer:

 func NewChatServer() *ChatServer {
 	return &ChatServer{
-		clients:   make(map[*Client]bool),
-		broadcast: make(chan string),
+		clients: make(map[*Client]bool),
 	}
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e7cfc4 and f22a526.

📒 Files selected for processing (2)
  • challenge-1/submissions/brenoamin/solution-template.go (1 hunks)
  • challenge-8/submissions/brenoamin/solution-template.go (1 hunks)
🔇 Additional comments (5)
challenge-1/submissions/brenoamin/solution-template.go (1)

1-24: LGTM!

The implementation is clean and straightforward. Error handling for input parsing is properly implemented, and the Sum function correctly returns the sum of two integers.

challenge-8/submissions/brenoamin/solution-template.go (4)

66-90: LGTM!

The Connect method correctly uses defer s.mu.Unlock() to ensure the mutex is always released, and properly validates username uniqueness before adding the client.


92-111: LGTM!

The Disconnect method correctly releases the server lock before acquiring the client lock, avoiding potential lock ordering issues. The check for !client.Disconnected before closing the channel prevents panics from double-close.


182-189: LGTM!

The error variables are properly defined and exported, following Go conventions.


24-37: LGTM with a minor note.

The non-blocking send pattern with select/default prevents deadlocks when the channel buffer is full, but silently drops messages. This is an acceptable trade-off for a chat server, though you may want to add a log statement in the default case for debugging purposes.

Comment on lines +121 to +126
sender.mu.Lock()
if sender.Disconnected {
log.Printf("[broadcast-ignored] sender=%s reason=disconnected", sender.Username)
return
}
sender.mu.Unlock()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Mutex deadlock on early return.

When sender.Disconnected is true, the function returns at line 124 without releasing the lock acquired at line 121. This will cause a deadlock on any subsequent operation involving sender.

 	sender.mu.Lock()
 	if sender.Disconnected {
 		log.Printf("[broadcast-ignored] sender=%s reason=disconnected", sender.Username)
+		sender.mu.Unlock()
 		return
 	}
 	sender.mu.Unlock()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
sender.mu.Lock()
if sender.Disconnected {
log.Printf("[broadcast-ignored] sender=%s reason=disconnected", sender.Username)
return
}
sender.mu.Unlock()
sender.mu.Lock()
if sender.Disconnected {
log.Printf("[broadcast-ignored] sender=%s reason=disconnected", sender.Username)
sender.mu.Unlock()
return
}
sender.mu.Unlock()
🤖 Prompt for AI Agents
In challenge-8/submissions/brenoamin/solution-template.go around lines 121 to
126, the code acquires sender.mu.Lock() then returns early when
sender.Disconnected is true, causing a mutex deadlock; fix by ensuring the mutex
is always released before any return — either check sender.Disconnected without
holding the lock (move the check before acquiring the mutex), or immediately
defer sender.mu.Unlock() right after locking so the lock is released on all
return paths, and remove the premature return while locked.

Comment on lines +149 to +153
sender.mu.Lock()
if sender.Disconnected {
return ErrSenderDisconnected
}
sender.mu.Unlock()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Mutex deadlock on early return.

Same issue as in Broadcast: when sender.Disconnected is true, the function returns at line 151 without releasing the lock acquired at line 149.

 	sender.mu.Lock()
 	if sender.Disconnected {
+		sender.mu.Unlock()
 		return ErrSenderDisconnected
 	}
 	sender.mu.Unlock()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
sender.mu.Lock()
if sender.Disconnected {
return ErrSenderDisconnected
}
sender.mu.Unlock()
sender.mu.Lock()
if sender.Disconnected {
sender.mu.Unlock()
return ErrSenderDisconnected
}
sender.mu.Unlock()
🤖 Prompt for AI Agents
In challenge-8/submissions/brenoamin/solution-template.go around lines 149 to
153, the code acquires sender.mu.Lock() then returns early when
sender.Disconnected is true, causing a mutex deadlock; fix by ensuring the lock
is always released before any return — either unlock explicitly before returning
or use defer sender.mu.Unlock() immediately after acquiring the lock so the
mutex is released on all control paths.

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