1. At about 11am this morning, we exceeded the 10,000 Yelp queries per
day allowed by our Yelp API key. (All copies of Ubiquity are using a
single API key... that's not good.)
2. Currently *every nountype* is getting queried on *every keystroke*.
(Technically, not quite every keystroke if you're typing fast; but
every time that the suggestion list is updated, which I think is every
250ms or once per keystroke, whichever is slower.)
3. Since the "restaurant" nountype queries Yelp, this means that those
calls to yelp are happening even if the user *has no intention of
using the Yelp command*.
4. Because of 1-3, it's easy to see how we could exceed the 10,000
yelp queries per day very, very quickly: that's a limit of
essentially 10,000 ubiquity input keystrokes across *all Ubiquity
users*.
5. Edwin today pointed out, in this thread:
http://groups.google.com/group/ubiquity-firefox/browse_thread/thread/8006d17f3edb3859#
that he uses custom async noun-types and they are getting hammered by
the constant queries... to the point where he asked for the option to
make his nountypes opt-out of general noun-type recognition.
The reason for querying all noun types on all input is so that we can
recognize the nountypes of the input and suggest appropriate verbs
based on those nountypes. There are three cases where this is
important:
* Languages where the nouns normally come first
* Cases where the user has a selection and the input is empty (Either
because they hit the delete key or because this is a context-menu
interaction.) Either way, the selection alone is what we are using to
base suggestions on.
* Cases where the user just happens to type a noun for input rather than a verb.
The following are the strategies that I can think of for reducing the
number of noun network queries.
1. Space them out with timing. Even if we update the suggestion list
on every keystroke/every 250ms, there is no sense in doing network
queries that often - if it takes 1 second for the network query to
return (under ideal circumstances), then there's no point calling it
more often than 1/second. So we could put in a 1/second maximum on
network-noun calls separate from the 250ms maximum on suggestion list
updates. That saves us a factor of 4.
2. There's little point in querying *all* nountypes when the input
doesn't fit one of the cases above. For instance, if the user's input
includes a verb, there's no reason to query all nountypes--only the
nountypes for that verb's specific arguments. In Parser1, we never
called the code that queries all nountypes except when the input was
empty or when the input matched no verbs at all. Parser2 seems to be
querying all nountypes in all circumstances -- which ends up producing
a lot of suggestions that get ranked off the bottom of the list
anyway, because if the input is "tran" then we can and should be
giving "translate" as the top suggestion, not anything nountype-based.
3. We can be cacheing async noun call results much more aggressively.
In most cases, whether an input matches a nountype or not does not
change -- "pizza" is not going to suddenly stop being a type of
restaurant. The *suggestions* may change, but the *fact that there
are suggestions*, which is all we need in order to recognize a
nountype, does not change. So we could cache that, perhaps outside
the nountype itself.
4. Finally, in the longer term we can use Aza's idea of building our
own server that proxies the nountype calls. That way if one user
queries us to ask whether "pizza" is a kind of restaurant, then the
next 10,000 users who query "pizza" will hit our cache. This could
cut our calls down dramatically, but it requires a lot of work to put
up such a server so it's not a short term solution.
What do people think? Are there more ways to reduce the number of
nountype network calls? Which ones of these should we pursue? How
urgent is this relative to the other things that we need to be doing?
--Jono
Mixing noun-first suggs with normal suggs is a bad idea. When you know
your task, seeing irrelevant suggs is nothing but annoying.
1. Space them out with timing. Even if we update the suggestion list
on every keystroke/every 250ms, there is no sense in doing network
queries that often - if it takes 1 second for the network query to
return (under ideal circumstances), then there's no point calling it
more often than 1/second. So we could put in a 1/second maximum on
network-noun calls separate from the 250ms maximum on suggestion list
updates. That saves us a factor of 4.
2. There's little point in querying *all* nountypes when the input
doesn't fit one of the cases above. For instance, if the user's input
includes a verb, there's no reason to query all nountypes--only the
nountypes for that verb's specific arguments. In Parser1, we never
called the code that queries all nountypes except when the input was
empty or when the input matched no verbs at all. Parser2 seems to be
querying all nountypes in all circumstances -- which ends up producing
a lot of suggestions that get ranked off the bottom of the list
anyway, because if the input is "tran" then we can and should be
giving "translate" as the top suggestion, not anything nountype-based.
3. We can be cacheing async noun call results much more aggressively.
In most cases, whether an input matches a nountype or not does not
change -- "pizza" is not going to suddenly stop being a type of
restaurant. The *suggestions* may change, but the *fact that there
are suggestions*, which is all we need in order to recognize a
nountype, does not change. So we could cache that, perhaps outside
the nountype itself.
How
urgent is this relative to the other things that we need to be doing?
Agreed!
> 1. Space them out with timing.
Doing #3 (caching) will mean this wouldn't be necessary.
> 2. There's little point in querying *all* nountypes when the input
> doesn't fit one of the cases above.
Agreed - that's kinda silly if its doing that.
> 3. We can be cacheing async noun call results much more aggressively.
Not just can - SHOULD (or even NEED). Even if we had #4 (our own
server), client-side caching will eventually be required.
> 4. Finally, in the longer term we can use Aza's idea of building our
> own server that proxies the nountype calls.
I like. This could also mean a simplification of client-side code, and
help with the service provider model that's been discussed recently (by
having the server translate results from different services into a
standard format).
> Which ones of these should we pursue?
#3 (caching) seems the best bet to me - not amazingly hard to implement,
and will help both short-term and long-term. #2 sounds like a regression
bug to me. #4 is a good longer-term plan, and sounds like it would need
a lot of IT investment.
Another option is brokering deals with relevant service providers to
give our API keys less restricted access, in exchange for bringing them
extra business. Of course, that only works with providers that have the
resources available, not smaller providers (or command-author supplied
services).
- Blair
I'd leave it to nouns themselves as some of them wouldn't like
external caching (async suggs aren't limited to ajax).
They should already be caching if they do high-cost async calls anyway.
Consider that the noun N knows the input P and U will be the same
result and has P's result already:
* If noun-caching, N returns the cached result for U.
* If parser-caching, the parser has to force N to retrieve U's result
since it doesn't know the relation.
Also note that nouns are singletons, but there can be multiple parser instances.
I guess for simplest cases, NounUtils can provide something that
prevents re-implementations.
- Blair
Yes, this sounds great. It does mean that some of the great things we
can do with asynchronous nountypes, like detecting locations, etc.,
and offering the map command, say, would not work (in English or in
Japanese) but I think it's a good compromise. I can work on trying to
implement this sort of dichotomy today, hopefully touching base with
Brandon to get his thoughts on how to implement it as well.
m
--
mitcho (Michael 芳貴 Erlewine)
mit...@mitcho.com
http://mitcho.com/
linguist, coder, teacher
Parser 2 has two types of parses:
1. parses with verbs (from prefixes it found at the beginning or end
of the input)
2. parses w/o verbs, aka argument-first parses/suggestions
For (1) parses it will now only check the argument strings against the
nountypes needed by that specific verb. For (2) parses, it will only
check nountypes with a special noExternalCalls = true property. satyr,
Brandon, and I agreed that this opt-in strategy for nountypes is a
safer option in this situation, as we'd like to err on the side of
less API calls.
There are a few other things we should consider in the near future.
For example, what if a nountype is async but also returns an immediate
suggestion which should be used in the argument-first suggestions?
satyr and I discussed possibly checking for an immediateSuggest
method, but it'd be good to get some more thoughts and feedback before
we think about this and other use cases.
In the mean time, however, this parser change should greatly reduce
the number of API calls made. If we get some noun-internal caching in
0.5 as well, we hopefully will be able to stem the tide.
mitcho
ps: Happy Firefox 3.5 day! :D
> mitcho (Michael 芳貴 Erlewine)