Livestream Chat

High-traffic channels like livestreams can generate thousands of messages per minute. The SDK provides configuration options to optimize memory usage and performance for these scenarios.

Message Limiting

The MessageLimitConfig allows you to control how many messages are kept in memory for different channel types. When the message count exceeds the configured limit, older messages are automatically trimmed from memory while keeping the most recent ones.

Configuration

Configure message limits when setting up the StatePlugin:

val statePluginFactory = StreamStatePluginFactory(
    config = StatePluginConfig(
        messageLimitConfig = MessageLimitConfig(
            channelMessageLimits = setOf(
                // Limit livestream channels to 500 messages in memory
                ChannelMessageLimit(channelType = "livestream", baseLimit = 500),
                // Limit messaging channels to 1000 messages in memory
                ChannelMessageLimit(channelType = "messaging", baseLimit = 1000),
            ),
        ),
    ),
    appContext = context,
)

ChatClient.Builder(apiKey, context)
    .withPlugins(statePluginFactory)
    .build()

How It Works

  • When the message count exceeds baseLimit + 30 (buffer), the SDK trims messages down to baseLimit
  • Messages are sorted by creation time; only the most recent ones are kept
  • Trimming only occurs when not loading older messages (to avoid interfering with pagination)
  • If loading older messages causes the count to exceed the limit, the limit is temporarily increased by 1.5x
  • The endOfOlderMessages flag is set to false when trimming occurs, indicating more messages are available but not yet loaded in memory
  • Messages are only removed from in-memory state, not from the local database

UI Integration

If you're using Jetpack Compose, the MessageList component works seamlessly with message limiting. Compose's LazyColumn only renders visible items, providing efficient virtualization automatically:

MessageList(
    viewModel = messageListViewModel,
    // Messages are automatically limited based on your MessageLimitConfig
)

For custom implementations, observe the message state from ChannelState:

val channelState: StateFlow<ChannelState?> = chatClient.watchChannelAsState(cid, messageLimit = 30)

channelState
    .filterNotNull()
    .flatMapLatest { it.messages }
    .collectLatest { messages ->
        // Messages are already limited based on your configuration
        updateUI(messages)
    }

Skipping Database Storage

For channels where message persistence is not needed (e.g., ephemeral livestream messages), you can configure the OfflinePlugin to skip database storage entirely. This reduces disk I/O and improves performance.

Configuration

Configure ignored channel types when setting up the OfflinePlugin:

val offlinePluginFactory = StreamOfflinePluginFactory(
    appContext = context,
    // Messages in these channel types won't be stored in the local database
    ignoredChannelTypes = setOf("livestream"),
)

ChatClient.Builder(apiKey, context)
    .withPlugins(statePluginFactory, offlinePluginFactory)
    .build()

Behavior

When a channel type is in ignoredChannelTypes:

  • Messages are not persisted to the local database
  • Reactions in these channels are not stored locally
  • The channel still receives real-time updates via WebSocket

This is useful for high-volume channels where:

  • Messages are ephemeral and don't need offline access
  • Reducing database writes improves performance
  • Storage space is a concern

Complete Setup Example

Here's a complete example combining both optimizations for a livestream use case:

// Configure state plugin with message limiting
val statePluginFactory = StreamStatePluginFactory(
    config = StatePluginConfig(
        messageLimitConfig = MessageLimitConfig(
            channelMessageLimits = setOf(
                ChannelMessageLimit(channelType = "livestream", baseLimit = 500),
            ),
        ),
    ),
    appContext = context,
)

// Configure offline plugin to skip DB for livestream channels
val offlinePluginFactory = StreamOfflinePluginFactory(
    appContext = context,
    ignoredChannelTypes = setOf("livestream"),
)

// Build ChatClient with both plugins
ChatClient.Builder(apiKey, context)
    .withPlugins(statePluginFactory, offlinePluginFactory)
    .build()

With this configuration:

  • Livestream channels keep only the 500 most recent messages in memory
  • Messages are not written to the local database
  • Real-time updates continue to work normally
  • Memory and disk usage remain controlled even with high message volume