-
-
Notifications
You must be signed in to change notification settings - Fork 506
Performance Issue in loading subscriptions [in qlobber.js via redis persistence] #428
Comments
@behrad qlobber has focused on matching performance - once the subscriptions are added, are you getting a problem? |
@behrad you could profile/trace qlobber to see where it's spending it's time (start from https://github.com/davedoesdev/qlobber/blob/master/lib/qlobber.js#L300). It could be the string split or some place in |
Might be interesting to write a quick test to compare string split performance against just iterating through the string with indexOf and substr. |
Matching is great on my data (300K subs from 67K clients), I haven't read your trie code however I see there may be a tradeoff between insertion and super fast matching. However in a real world case, there will be even more clients (millions) and multiple millions of subscriptions. Not tested matching in that scale yet... Currently high ratio insertion is bottleneck. This problem shows up in two cases:
and client connects are online type of requests. Rate limiting seems not rational to me. |
will let you know. Is there any profiling module on npm you prefer that I use on qlobber? |
0x, a new thing we are building at nearForm.
|
@mcollina How dya think about this problem? May it be an architecture flaw? It doesn't feel scalable this way around. |
They are in 5 levels: |
0x looks cool - care to give it a try? |
Yes, sharded/clustered may help but I think it's worth doing some profiling and checking for deopt. |
I'm actually fine in using a patrica trie vs a normal trie. It does not Regarding the approach, it is scalable within a limit: without the
|
I changed I'm not a V8 expert and don't know about tail-recursion optimizations in node.js, but when I commented all assignments in https://github.com/davedoesdev/qlobber/blob/master/lib/qlobber.js#L123-L152 to let the code only recurse, it only took 30 Seconds! |
@behrad thanks for doing that. I'm keen to profile this myself (0x will be useful). I'm going to do this here: davedoesdev/qlobber#8 |
Found effective line: only commenting |
👍 @davedoesdev eager to this :) |
That should just be a hash table lookup, weird. |
Btw which version of Node are you using? |
Also, what if you change https://github.com/davedoesdev/qlobber/blob/master/lib/qlobber.js#L151 to return this._add(val, i + 1, words, st); ? |
I think TCO is coming in ES6. Not read the spec fully myself - may have to do something to support it? Also not sure that is the problem, just a guess. |
According to this, TCO not yet in Node: https://kangax.github.io/compat-table/es6/ Am thinking if it will be difficult to write this iteratively. |
On Node 4.3.1, will test with
That'd be great, I was thinking about asking you for an iterative version so that I can test against recursive one 👍 |
No, it took 14 minutes, but I think I got it. Changing the line to I believe an iterative version with an internal global object for the trie state, inside qlobber object, should enhance this much. |
@behrad how strange! I'd assumed V8 would just pass a pointer to the object! |
Btw- could you let me have your test script please? |
(or maybe it's lazy and it doesn't do the hash table lookup if it thinks it isn't needed - could try just accessing |
wouldn't it be feasible to wrap the recursion in an outer function and 2016-03-05 13:29 GMT+01:00 David Halls [email protected]:
|
I have none, it is my application code in my stress testing environment. You can easily reproduce this by subscribing 60K clientIds each subscribing to 5 topics with 5levels ( |
And how much dya think reducing topic levels will help? I haven't tested with 3levels topic names, since my app is designed to work on a 5 level convention in production, can't change this that simple |
I've not followed this much, I will catch up in the next few days. Unique insertion will be great. However allow to pass a function to test
|
@mcollina agreed |
I've found an optimisation for if (dest.length === 0) {
return origin.slice(0);
} |
👍
Will test if it works... |
The problem with unique insertion is how to do it without slowing down non-unique insertion. |
Regarding https://github.com/mcollina/mosca/blob/master/lib/persistence/redis.js#L107 Is this actually correct? What if there is a wildcard rule in |
wow i am getting curious whether all of this is related to #426 .. 2016-03-06 14:36 GMT+01:00 David Halls [email protected]:
|
This is Ok, since if multiple subscriptions of a clientId match, only a single message should be delivered I think :) @mcollina can approve this.
Mosca's all usage is unique insertion I believe, However this can be configured as a qlobber constructor option. |
Agreed, this is expected behavior, but have a look at #426 this does not currently work with redis |
@behrad but what if the wildcard subscription is removed? Remove and add won't be balanced. Anyway, best way is to add uniqueness into qlobber. Ideally it would be able to use a list (as now) or a set on each leaf. ES6 has sets. |
yes, and remove/add won't work yes!
Is it going to happen any soon? so that I can test against my current setup? |
this reduce 78seconds to 17seconds for the above sample code |
@behrad is that good enough? |
Have done some prep work on qlobber's master branch (not on npm). I've pulled out the list-specific handling into separate methods. |
@behrad qlobber 0.6.0 is now published, which includes QlobberDedup: https://github.com/davedoesdev/qlobber#qlobberdedupoptions API is the same except for It'd be good to know if it helps your use case any. You'll have to modify mosca to use it. |
I found currently Mosca is forwarding duplicate messages to client if overlapping topics are subscribed even now! It may be related to #428 (comment) I think this may be a better Idea not to handle duplication on |
Was not good enough in mosca's case in my tests
I'll give it a try 👍 |
@behrad Let's sort this:
The latter is what #426 is about; I have not found time to dig into this deeper yet. |
what do you mean by exact? If got it right, it's more performant to do it as a simple stringish validation at very start of subscribe method. This is heavy to pass this to an internal huge data structure like Qlobbers trie |
I mean:
|
right then it's more cheap to do it as a validation in subscribe before sending it down inside |
Btw the benchmark results for
You may be able to get quicker by using a separate hashtable-based structure for checking added duplicates. Pure matching is about the same as the normal (non-deduplicating) |
@behrad what did you decide to do (if anything)? |
Sorry for my late answer @davedoesdev Was our busy days and we are in Norouz holidays now :) |
Finally I'm going to catch some time to spend on this guys. |
Regarding http://250bpm.com/blog:19, qlobber already stores strings on nodes in the trie. |
Fixed in v2. |
I have 67K subscribers with 5 topics subscribed each (around 335K subscriptions), when starting Mosca it takes minutes for it to start persistence on redis (to load subscriptions) and CPU is 100%
I traced the code and found this line https://github.com/mcollina/mosca/blob/master/lib/persistence/redis.js#L108 is producing all the trouble. commenting it lets mosca to start in few seconds iterating all the keys via
keys ...*
command.So it seems qlobber trie implementation is not scalable to many hundred thousands of additions.
Asking @davedoesdev to think about performance improvements?
Change qlobber with may be another trie/qlobber impl? I found some but not dig into them yet:
https://github.com/isaacs/node-glob
https://github.com/jeresig/trie-js
https://github.com/rbranson/glob-trie.js?utm_source=javascriptweekly&utm_medium=email
http://ejohn.org/blog/javascript-trie-performance-analysis/
Writing a custom one?
What do you suggest @mcollina ?
The text was updated successfully, but these errors were encountered: