Live event rate limiting
Live events with thousands or millions of concurrent viewers create unique challenges for real-time systems. When too many messages flood a single channel, the chat becomes unreadable and the user experience suffers.
PubNub's Functions-based rate limiter provides dynamic message throttling that maintains optimal user experience while preserving the feeling of being part of a massive live audience. This is the recommended approach for most high-occupancy live events.
For events with natural audience divisions (such as different languages or team affiliations), channel sharding offers an alternative approach that creates separate conversation spaces.
By the end of this document, you will understand:
- How message frequency affects user experience
- How the Functions rate limiter maintains engagement while controlling message flow
- When channel sharding makes sense (logical groupings only)
- Best practices for live event messaging
How PubNub helps
| Role in the solution | PubNub feature (click to learn more!) |
|---|---|
| Functions |
| Pub/Sub |
| Presence |
Use case overview
Recommended approach
The Functions-based rate limiter is the recommended solution for most live events. It maintains the feeling of being part of a massive shared experience while controlling message flow to keep chat readable.
When 100,000 fans are celebrating together, you want them to feel the energy of the crowd—not feel separated into smaller rooms. The Functions rate limiter achieves this by intelligently throttling message rates while keeping all users in a single shared channel.
This approach:
- Preserves the "stadium atmosphere" where users feel part of the full audience
- Dynamically adjusts throttling based on current message rates
- Requires no user separation or channel assignment logic
- Works automatically with minimal configuration
For complete implementation details and sample code, see Rate Limiting.
Consider a live soccer match between Chelsea FC and Southampton FC streamed to 100,000 viewers. As exciting moments happen, such as a controversial call or someone scoring an absolute screamer, thousands of fans want to share their reactions simultaneously. Without rate limiting, this creates several problems:
- Chat becomes unreadable when hundreds of messages appear per second
- High message volume makes it impossible to follow conversations
- Participants can't see their own messages or engage meaningfully
The Functions rate limiter solves these challenges by intelligently throttling message rates while keeping all 100,000 fans in a shared experience. Users feel like they're part of a massive stadium crowd, not separated into smaller rooms.
Message frequency and readability
Real-time systems can easily become "noisy" when too many participants send messages simultaneously. In use cases where audience dialogue is important, too much noise lowers attention span until the chat reaches a saturation point that makes meaningful conversation impossible.
The saturation point varies depending on your use case.
| Outcome | Recommended message rate | Experience type |
|---|---|---|
| Meaningful conversations between attendees | Lower message rates | Dialogue-focused experiences |
| Excitement and energy; conversation is less of a priority | Higher message rates | Engagement-focused experiences |
Channel sharding
When to use channel sharding
Channel sharding is appropriate only when logical groupings exist, such as different languages, team affiliations, or geographic regions. If there's no natural way to divide your audience, use the Functions rate limiter instead to preserve the shared experience.
Channel sharding divides your audience into separate conversation spaces. Unlike the Functions rate limiter, this approach separates users into distinct rooms rather than keeping them in a shared experience.
Channel sharding makes sense when there are logical groupings, such as:
- Language-based separation, such as Spanish-speaking fans chat with other Spanish speakers
- Team affiliation, such as Home and away fan sections
- Geographic regions, such as Local fan communities
- Premium tiers, such as VIP rooms for special access
When to avoid channel sharding:
- You want users to feel part of the full audience ("100k stadium" experience)
- There's no logical way to group users
- Random distribution would fragment the community feel
Considerations
When implementing channel sharding with logical groupings, consider these trade-offs.
| Advantages | Considerations |
|---|---|
| Creates relevant conversation groups | Users in different shards have different experiences |
| All messages reach participants in the group | Requires monitoring occupancy if audience size is unpredictable |
| Logical groupings improve engagement and "stickiness" | Channel rebalancing may be needed if users leave unevenly |
| Ideal for language or team-based separation | Additional logic needed for channel assignment |
Channel sharding implementation
If you've determined that channel sharding is appropriate for your use case (because you have logical groupings), here's how to implement it.
Logical grouping
The only recommended sharding approach groups users by logical criteria that enhance the experience:
1// Example: Shard by language preference
2async function assignChannelByLanguage(userId, language) {
3 const channelId = `game.chat.${language}`;
4
5 await pubnub.subscribe({
6 channels: [channelId]
7 });
8
9 return channelId;
10}
11
12// Usage
13const userChannel = await assignChannelByLanguage("user-123", "es");
14// Result: "game.chat.es" for Spanish-speaking users
Common logical groupings for sports and entertainment:
- Language - separate channels for different languages
- Geographic region - regional channels for local fans
- Team affiliation - separate home and away team fan channels
- Experience level - channels for casual and hardcore fans
This approach improves both cost efficiency and user experience by creating more relevant conversation groups. For example, users who speak Portuguese can chat with other Portuguese-speaking users, and users who are hardcore fans of Chelsea FC can chat with other hardcore fans of Chelsea FC.
Dynamic assignment with Presence monitoring
For logical groupings with unpredictable audience sizes, use Presence to monitor occupancy within each logical group.
1// Monitor channel occupancy and assign users to balanced channels
2async function assignToDynamicChannel(userId, targetOccupancy = 1000) {
3 const baseChannel = "game.chat";
4 let assignedChannel = null;
5 let shardIndex = 0;
6
7 // Check channels until we find one below target occupancy
8 while (!assignedChannel) {
9 const channelId = `${baseChannel}.shard-${shardIndex}`;
10
11 try {
12 // Check current occupancy using Presence
13 const response = await pubnub.hereNow({
14 channels: [channelId]
15 });
show all 38 lines| Advantages | Considerations |
|---|---|
| Adapts to changing audience sizes | Server-side monitoring of channel occupancy |
| Maintains balanced occupancy across channels | Additional logic to handle channel rebalancing |
| Creates new channels automatically as needed | Management of users leaving channels |
Monitor channel health
Once your sharded channels are running, monitor their performance to ensure optimal user experience.
1// Monitor occupancy across all shards
2async function monitorShardOccupancy(baseChannel, shardCount) {
3 const channels = Array.from(
4 { length: shardCount },
5 (_, i) => `${baseChannel}.shard-${i}`
6 );
7
8 const response = await pubnub.hereNow({
9 channels: channels,
10 includeState: false
11 });
12
13 // Analyze occupancy distribution
14 const occupancyReport = channels.map(channelId => ({
15 channel: channelId,
show all 26 linesKey metrics
| Metric | Purpose |
|---|---|
| Average occupancy per shard | Ensure channels aren't becoming too crowded. |
| Occupancy distribution | Identify imbalanced shards that may need adjustment. |
| Message rate per shard | Verify that message frequency remains readable. |
| User feedback | Monitor complaints about chat speed or visibility. |
Best practices
When implementing rate limiting for live events, follow these best practices.
Choose the right approach
Start by determining whether you need channel sharding or the Functions rate limiter.
| Situation | Recommended approach |
|---|---|
| You want users to feel part of the full audience | Functions rate limiter |
| You have natural audience divisions (language, team, region) | Channel sharding |
| You're unsure | Start with Functions rate limiter |
Most live events benefit from the Functions rate limiter because it preserves the shared experience of being part of a massive audience.
Channel sharding: use logical groupings only
Only implement channel sharding when you have meaningful ways to group users. Logical groupings (language, region, team) create more engaging experiences. Random distribution fragments the community feel and we generallydon't recommend it.
Channel sharding: plan for uneven departures
Users don't leave evenly across shards. Some channels may empty while others remain active. Consider the following:
- Migrating users from emptying channels to active ones
- Accepting that some channels will have different occupancy
- Focusing on the average experience rather than perfect balance
Test at scale
Before your event, simulate expected audience sizes, test your rate limiting configuration, verify monitoring performance, and ensure the experience meets your goals.
Consider complementary features
Rate limiting works well alongside other features:
- Content moderation for chat safety
- Broadcast channels (separate channels for official announcements that reach all users)
- VIP rooms (special channels for premium users), a natural logical grouping for sharding
Additional resources
For more details on the approaches covered in this document, refer to:
- Complete implementation guide for the Functions-based rate limiter
- PubNub serverless Functions documentation
- Client-side Subscribe Filters for message filtering
Football club names are used for illustrative purposes only and do not imply any affiliation or endorsement.