Add a TaskTree ApplicableNodeFilter

The ApplicableNodeFilter is able to determine whether a node can cache
the applicability of some of its subnodes by Branch. If so, it will
cache this applicability for each Branch which it encounters, and reuse
the results when it encounters the same branch again. This potentially
reduces the rescanning of large numbers of subnodes for each Change down
to rescanning those nodes only for each Branch.

Due to the nature of a global server task config, task applicability is
often determined by a Change's project and branch, so it would not be
unusual for this per Branch caching to benefit large task configs. It is
relevant to note that since ChangeNodes are not cached, this change does
not improve walking dependencies. Further more sophisticated
improvements are needed to noticeably improve the "walking" use case.

In a real world use case, there is a root task with over 1K subtasks and
only 1 or 2 of those subtasks is almost ever applicable to a Change. The
applicability in these cases is almost exclusively determined by
'destination' and `destination like` queries which are based on the
Change's destination. In this case, when the PW root task is being
evaluated on a Change destined for an already seen Branch, instead of
having to evaluate over 1K subnodes against the Change to see if they
are applicable, the 1 or 2 applicable subnodes can be returned from the
cache using a simple Branch lookup in a Map.

With this change, there is a significant performance increase of the
following ssh non change walking query:

 status:open --task--applicable --format json --no-limit

Before this change: 1m37s, 1m42s, 1m37s
After this change:  1m11s, 1m10s, 1m14s

Change-Id: I4c3ff9ceb990386e0f0d11fbd55cdc825c677e4c
3 files changed