Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nsqd: questions for large number of topics/channels #577

Merged
merged 3 commits into from
May 3, 2015

Conversation

mreiferson
Copy link
Member

Background: We need to dispatch user messages to a specific topic, where every consumer only subscribe the message for a fixed range of user. Since nsqd doesn't support partitions by key, we created thousands of topics/channels, i.e. user_topic_[0-999], one topic for a range of users.

Let's suppose there will be 10K topics and channels, we want to know that whether nsqd will work well or not in this situations.

  1. file number limit of process. This is easy to increase it.
  2. memory consumption? After some trying, seems not so much memory consumption.
  3. High cpu usage even if EMPTY workload. About 40% - 60% for one nsqd process, while no producer or consumer connect to it. strace tells there are lots of futex syscall.
    @xiaost helps to troubleshoot, it may be caused by the 100ms ticker for every channel, and maybe we need to make it configurable?
  4. slow startup. @xiaost already submited a PR nsqd: skip persist metadata while loading #575
  5. Any other potential problems?

Thanks

@xiaost
Copy link
Contributor

xiaost commented Apr 26, 2015

High cpu usage even if EMPTY workload. About 40% - 60% for one nsqd process, while no producer or consumer connect to it. strace tells there are lots of futex syscall. @xiaost helps to troubleshoot, it may be caused by the 100ms ticker for every channel, and maybe we need to make it configurable?

https://github.com/bitly/nsq/blob/v0.3.2/nsqd/channel.go#L18

@mreiferson
Copy link
Member

  1. High cpu usage even if EMPTY workload. About 40% - 60% for one nsqd process, while no producer or consumer connect to it. strace tells there are lots of futex syscall. @xiaost helps to troubleshoot, it may be caused by the 100ms ticker for every channel, and maybe we need to make it configurable?

Can you supply a CPU profile dump from /debug/pprof/profile?

  1. Any other potential problems?

Nothing that I can think of other than CPU/memory. Are you running nsqd with GOMAXPROCS > 1?

@mreiferson mreiferson changed the title nsqd: problems of large number of topics/channels nsqd: questions for large number of topics/channels Apr 26, 2015
@mreiferson
Copy link
Member

Grabbed a profile with 1000 topics, each with a single channel, no clients:

screen shot 2015-04-26 at 4 45 19 pm

As @xiaost discovered, it's clearly the channel (and diskqueue) timers. An easy fix is to add configuration to extend this delay. A better fix is to come up with a better strategy for either:

  1. exponentially backing off the delay if there isn't consecutive work to do
  2. coming up with a scalable/performant signaling mechanism to stop the timers when theres no work to do and re-enable them when there is

Thoughts?

@allenlz
Copy link
Author

allenlz commented Apr 27, 2015

Are you running nsqd with GOMAXPROCS > 1

Yes. GOMAXPROCS is 4

An easy fix is to add configuration to extend this delay.

After I build a temporary nsqd with it set to 1000ms, the cpu usage dropped from 40-50% to 7%.

IMHO, signaling mechanism seems a good idea.

And I will let you know if I meet more problems.

@mreiferson
Copy link
Member

IMHO, signaling mechanism seems a good idea.

And I will let you know if I meet more problems.

@allenlz are you going to take a pass at submitting a PR for this?

@mreiferson
Copy link
Member

@allenlz @xiaost what do you think of this approach (see commit msg):

mreiferson@1755bcc

@xiaost
Copy link
Contributor

xiaost commented Apr 29, 2015

@mreiferson looks good! 👍

why not use shuffle to pick up channels instead of uniqRands:
xiaost@b3f6b1a

@mreiferson
Copy link
Member

@xiaost the difference is in my approach we generate a constant sized random []int rather than a permutation the length of the number of channels followed by a re-ordering of the slice.

@xiaost
Copy link
Contributor

xiaost commented Apr 29, 2015

@mreiferson uniqRands looks not so effective if we generate [20]int in 21 channels. 😢

what about:

        if num < len(channels) { // need shuffle
            for i := 0; i < num; i++ {
                j := rand.Intn(len(channels) - i)
                channels[i], channels[i+j] = channels[i+j], channels[i]
            }
        } else {
            num = len(channels)
        }

xiaost@2c27890

I suppose it is identical In probability

@mreiferson
Copy link
Member

I'm not sure I see the benefit.

I am going to add a pool of goroutines (that grows according to the number of channels, with a configurable max) to actually do the work. This way, for large numbers of channels, it can be parallelized (like it currently is).

@mreiferson
Copy link
Member

opened a PR here w/ the pool of workers for concurrency

RFR @allenlz @xiaost @jehiah

}
}

if float64(numDirty)/float64(num) > n.opts.QueueGCDirtyPercent {
Copy link
Member

Choose a reason for hiding this comment

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

What does this mean for a case where you have 1 hot channel (with a large deferred queue or something) and lots of quiet/idle channels. Should we loop on the same channels to drain them instead of picking different ones each loop?

That would mean that we will be actively draining priority queues (25% of them a time up to max QueueGCPoolMax) and only go back to the random selection if the one we were draining is empty. This would keep QueueGCInterval from being an accidental cap of how fast we can drain priority queues of QueueGCPoolMax per QueueGCInterval in that edge case.

Copy link
Member Author

Choose a reason for hiding this comment

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

Each iteration should "drain" the deferred queue in the sense that there was no more work to do now. There might still be more in the queue that have not timed out. Does this clarify anything? Do you think we still need to somehow prioritize busier queues?

Copy link
Member

Choose a reason for hiding this comment

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

ahh. the loop inside processInFlightQueue is what i was missing. It works as i was suggesting then. (I mistakenly thought it only pop'd one message each time that channel got pushed into workCh)

@mreiferson
Copy link
Member

@jehiah updated names, bit of re-org, added comments and fixed up the exit paths PTAL

@jehiah
Copy link
Member

jehiah commented May 3, 2015

LGTM. you ready for this to land?

@mreiferson
Copy link
Member

Yes, I'll 🔨

mreiferson added 3 commits May 3, 2015 11:57
This moves to a single goroutine to process in-flight and deferred
priority queues.  It manages a pool of workers (configurable max) that
process channels concurrently.

It is a copy of redis's probabilistic algorithm that wakes up every 100ms
to select a random 20 channels from a locally cached list (refreshed
every 5s).

If either of the queues had work to do the channel is considered "dirty".

If 25% of the selected channels were dirty, the loop continues without
sleep.

For 1000 topics and channels with no clients connected - idle cpu usage
dropped to ~8%.
@mreiferson mreiferson force-pushed the central_worker_577 branch from 0211726 to cd1a2f1 Compare May 3, 2015 18:57
@mreiferson
Copy link
Member

🔥 👊

jehiah added a commit that referenced this pull request May 3, 2015
nsqd: questions for large number of topics/channels
@jehiah jehiah merged commit 668a8af into nsqio:master May 3, 2015
@mreiferson mreiferson deleted the central_worker_577 branch May 3, 2015 19:38
@allenlz
Copy link
Author

allenlz commented May 7, 2015

Today, we met another problem about large number topics/channels. We found nslookupd consumes a lot of cpu. Please refer to #584

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants