Ticket #288 (accepted enhancement)
[PATCH] Allow smarter node creation based on visibility during createtree
| Reported by: | capnbry@… | Owned by: | jow |
|---|---|---|---|
| Priority: | major | Milestone: | |
| Component: | LuCI Base | Keywords: | _create_node,ram,dispatcher,tree |
| Cc: |
Description
As I've brought up on the mailing list thread "High latency caused by full tree creation", there is a large amount of delay per LuCI request which is spent building the node tree in createtree(). Most nodes created aren't needed for the view presented to the user and only serve to consume memory and CPU time during a page load.
My idea is to provide an easy mechanism for index()ers to determine which needs to be created and what isn't. Due to the constraints of the standard LuCI web interface, this optimization needs to establish a few rules:
- The page requested must have its node created
- All parents of the page being requested must be created, so the children inherit the track
- All the top-level nodes "Status", "System", "Services", "Network" (and others added by extensions) must be created in order to have their top-level tabs in the UI
- All peers of second-level nodes need to be created as well for the same reason, to display their links on the subindexes
To make this easy to implement in each controller, the attached patch adds an "inreq" field to each created node to indicate if it lies on the request path. To satisfy the "top level node" requirement, we always add the top level node, then check its inreq property if the top-level node is not "in request", then the controller can exit index() early.
Now that we know we're in the right top level node, we can then use the same methodology per subindex. Imagine if the menus were classifications of animals
if not entry({"animal"}, template("animals"), i8n("Animals"), 10).inreq then return end
if entry({"animal", "mammals"}, template("mammals"), i8n("Mammals"), 10).inreq then
entry({"animal", "mammals", "humans"})
entry({"animal", "mammals", "dolphins"})
entry({"animal", "mammals", "monkeys"})
end
if entry({"animal", "fish"}, template("fish"), i8n("Fish"), 20).inreq then
entry({"animal", "fish", "tuna"})
entry({"animal", "fish", "dolphins"})
entry({"animal", "fish", "bass"})
end
This is pretty easy to use and doesn't break existing code, relying on the user to check the inreq field and omit creating nodes as necessary.
Attached patch adds this flag (to save memory it relies on nil = false so only nodes in the request path have an inreq entry in their tables).
The urltokens are removed from the request first, to prevent duplication of checking each entry of the request path for tokens which would add ridiculous overhead. If this is not acceptable to do it in httpdispatch(), I suggest that the code be moved to dispatch() before createtree() because this optimization is significant to both reduce complexity and execution time.
I've also attached a quick patch against admin-full to see it in action. The number you're looking at here is the "http waiting time" or the time between when the browser requests the page and when the web server starts sending data. I'm seeing reductions from 20-50% in waiting time (~200ms-500ms), as well as a large decrease in memory usage per request. This is just a first run of using the inreq flag; I didn't want to spend a few hours optimizing the admin pages if the patch would be completely rejected :)
Note this builds on #2476 patch's change to _create_node()

