In a standard Terminal User Interface (TUI), the screen is usually static.
To see new messages, a user would typically have to manually refresh or the app would have to "poll" the database every second , which is expensive and laggy.
By using Redis Publish and Subscription Method, we can implement a "Signal" pattern: the database handles the permanent storage, while Redis acts as the instant notification system that tells the TUI exactly when to re-render the messages once users send messages to one another in a chat room.
1. Installation and Setup
To follow this implementation, you need the Redis server running and the official .NET client.
Step 1: Run Redis via Docker
Instead of a complex local installation, use Docker to spin up a lightweight instance in your command prompt:
docker run --name tui-redis -p 6379:6379 -d redis
Step 2: Add the NuGet Package
In your C# project directory, install the StackExchange.Redis library via NuGet Package Manager.
dotnet add package StackExchange.Redis
2. The "Signal" Implementation
The core logic is split into two parts: the Sender (who publishes the signal) and the Receiver (who listens for the signal).
For my project architecture are as follows :
|
|-- Services/
| |-- RedisMessagingServices.cs (The Publisher)
|
|-- View/
| |-- ChatwithContactView.cs (The Subscriber & Renderer)
The Sender: Publishing the Refresh Event
In your messaging service, after you successfully insert a message into your SQL database, you immediately notify the receiver’s channel.
C#
// Inside RedisMessagingServices.cs
public bool insertMessage(string sender, string receiver, string content) {
if (db.Save(sender, receiver, content)) {
var sub = redis.GetSubscriber();
// Signal the specific receiver to refresh their view
sub.Publish($"messages:{receiver}", "REFRESH_CHAT");
return true;
}
}
The Receiver: Listening in the Background
In the TUI View, we subscribe to our own channel. This happens in the background, so the user can keep typing while the app waits for a signal.
C#
// Inside ChatwithContactView.cs
var sub = connectionMultiplexer.GetSubscriber();
sub.Subscribe($"messages:{currentUser}", (channel, message) => {
if (message.ToString() == "REFRESH_CHAT") {
_needRefresh = true; // Set the flag to trigger a re-render
}
});
3. The Reactive Render Loop
To make the TUI feel "live" without flickering, we use a while(true) loop that only updates the screen when the _needRefresh flag is true.
C#
while (true) {
if (_needRefresh) {
RenderFullChat(currentUser, contact, inputBuffer.ToString());
_needRefresh = false; // Reset the flag after rendering
}
// Check for non-blocking keyboard input
if (Console.KeyAvailable) {
// Handle typing logic...
}
Thread.Sleep(50); // Keep CPU usage low
}
Redis Architecture:
This optimization could be done with the help of Redis' Publish/Subscribe Architecture! When an event occurs , like a user sending a message or a team scoring a goal , your server publishes a message to that specific channel. Redis immediately pushes this message to every active subscriber, allowing the application to trigger a "refresh" or update the UI in real-time.
Why This Works
This approach combines the best of both worlds:
- Data Integrity: Your SQL database remains the "Source of Truth" for long-term message history.
-
Instant Experience: Redis
Pub/Subprovides sub-millisecond signaling, ensuring that the moment a message is sent, the receiver's terminal clears and re-renders with the new data. -
Resource Efficiency: By only re-rendering on a
REFRESH_CHATsignal, you avoid unnecessary CPU cycles and screen flickering, resulting in a professional-grade CLI experience.
Final Implementation Note:
Always remember to call sub.Unsubscribe(channelName) when the user exits the chat view. This ensures your application doesn't leave "ghost" subscriptions open in the background, keeping your Redis memory usage clean and optimized.
Top comments (0)