StackExchange / StackExchange/StackExchange.Redis

How to limit memory usage and number of threads of pub sub at 15k QPS?

Open
#2,781 7 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C#
Stars
6.2k
Forks
1.6k
Avg merge
1d 15h
Merged PRs (30d)
43

Description

Background

  • Problem: Memory usage keeps increasing when a client has subscribed to too many channels.
  • Scale:
    • Subscriptions: 3691 channels
    • Message frequency: 0 to 4 messages per second per channel
    • Peak network download rate: ~400 Mbps
    • Estimated 99th percentile latency of callback: ~100 microseconds
  • Environment:
    • .NET 6.0
    • StackExchange.Redis 2.7.33
    • ConnectionMultiplexer configuration:
      • ClientName
      • ReconnectRetryPolicy
      • SyncTimeout
      • AsyncTimeout

Attempted Solutions and Findings

  1. Garbage Collection: Forcing GC provides temporary relief but doesn't solve the issue.

  2. Memory Profiling: Survived objects are primarily Byte[] in ChannelMessageQueue.

  3. Redis Server Queue Limit: Configuring a max size for the queue at the Redis Server doesn't help.

  4. Source Code Digging: I see that ChannelMessageQueue creates an unbounded channel here. So we believe the SE Redis client is fetching from the server faster than our callbacks can process, thus accumulating a backlog on the client side.

  5. Upvoting a similar question on Stack Overflow.

  6. Process Splitting: The more processes we run for the same amount of subscriptions, the less combined memory usage and the higher CPU utilization rate.

    Here are some numbers at 80% of the scale mentioned above.
    As for the row for 4 processes, the memory growth only stops because the server queue has filled up and the server starts to disconnect the client.

    Number of Processes Peak Combined Memory (GB) Peak Thread Count
    4 37 302
    5 19 337
    8 13.3 507
    10 12.7 614
    40 12.2 2184
    ... ... ...

Questions and Considerations

  1. Memory Usage Limitation: What strategies can we employ to limit memory usage effectively? We prefer the client does not fetch items from the server fast enough and the server disconnects the client after the queue at the server exceeds a pre-configured size.
  2. Thread Count Optimization: How can we minimize the number of threads while maintaining performance?
  3. Allocation Optimization:
    • Current: StackExchange.Redis allocates memory on the heap for new payloads.
    • Hypothesis: Processing could be faster by reducing/eliminating heap allocation.
    • Potential approach: Direct processing of messages from OS socket without an intermediate queue buffer.

We appreciate any insights or recommendations to address these challenges. Thank you for your assistance!

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with src/StackExchange.Redis/ChannelMessageQueue.cs at line 96 and investigate the reported ChannelMessageQueue Byte[] backlog under the .NET 6 and StackExchange.Redis 2.7.33 workload. The issue provides measurements and hypotheses but no concrete code change or acceptance condition, so the expected definition of done would need to be established first.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, redis
Domain
backend, performance
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.