This patch bundles a few changes to component rendering and ActionPack in general. The resulting implementation is much faster for components, but also speeds up ActiveRecordStore for new sessions considerably, because session saving can be lazy again.
The code is based on the assumption that rendered components should use the session settings of their embedding controller. While it was previously allowed to embed a session requiring component into a page whithout session management, or with a session :off specification, this will no longer work with this patch.
I think this is a reasonable restriction.
Currently, a page embedding n components will cause (n+1) session loads and (n+1) session saves. If the page access creates a new session, this will result in an additional save operation. With the patch in place, at most 1 load and 1 save will be made.
Patch description:
Two attr_accessors are added to AbstractRequest: :parent_controller and :flash. In render_component the current request gets duped and parent_controller of the new request gets assigned the current controller embedding the render_component call. This accessor is then checked inside ActionController::process to determine whether the session needs to be saved, whether the flash needs to be swept and whether persistent model associations need to be cleared. When duping the request, the current session gets passed to the sub_request as well.
The implementation eliminates the fire_flash, sweep_flash and clear_persistent_record_associations filters. This is not essential and could be reverted if need be. But I wonder whether the current implementation behaves as intended: if a before filter fails, the flash will not be swept and the associations are not cleared. So if someone accesses the flash or an association in a filter, the behaviour might not be as originally intended. Could I get some feedback on this, please?
A few other changes are contained in the patch:
HashWithIndifferentAccess gets a regular_update method(other_hash), which is a lot faster than calling update(other_hash) and can be used if other_hash is known to be indifferent_access compliant.
AbstractRequest and CGIRequest use @env internally instead of env. This saves 2 function calls per env access. CGIRequest#method is cached as well.
Performance data:
First, a page embedding 5 components:
Tested with ActiveRecordStore:
perf data file 1: 01-17.comp.slow_components.ar_store
requests=1000, options=-bm=comp_test -fast_routes -fast_readers
-fast_connection -single_connection_cache_key -old_components
-lib=stablep
perf data file 2: 01-17.comp.fast_components.ar_store
requests=1000, options=-bm=comp_test -fast_routes -fast_readers
-fast_connection -single_connection_cache_key -old_components
-lib=stablep2
page c1 real c2 real c1 r/s c2 r/s c1 ms/r c2 ms/r c1/c2
/test/comp_test 5.92573 5.07233 168.8 197.1 5.93 5.07 1.17
With SQLSessionStore:
perf data file 1: 01-17.comp.slow_components
requests=1000, options=-bm=comp_test -mysql_session -fast_routes
-fast_readers -fast_connection -single_connection_cache_key
-old_components -lib=stablep
perf data file 2: 01-17.comp.fast_components
requests=1000, options=-bm=comp_test -mysql_session -fast_routes
-fast_readers -fast_connection -single_connection_cache_key
-old_components -lib=stablep2
page c1 real c2 real c1 r/s c2 r/s c1 ms/r c2 ms/r c1/c2
/test/comp_test 4.35981 3.63821 229.4 274.9 4.36 3.64 1.20
Comparing requests which don't use components show improvement as well:
perf data file 1: 01-17.all.slow_components.ar_store
requests=1000, options=-bm=all -fast_routes -fast_readers -fast_connection -single_connection
_cache_key -old_components -lib=stablep
perf data file 2: 01-17.all.fast_components.ar_store
requests=1000, options=-bm=all -fast_routes -fast_readers -fast_connection -single_connection
_cache_key -old_components -lib=stablep2
page c1 real c2 real c1 r/s c2 r/s c1 ms/r c2 ms/r c1/c2
/empty/index 3.64878 2.66773 274.1 374.9 3.65 2.67 1.37
/welcome/index 3.78321 2.81584 264.3 355.1 3.78 2.82 1.34
/rezept/index 2.94626 2.93406 339.4 340.8 2.95 2.93 1.00
/rezept/myknzlpzl 2.94806 2.93190 339.2 341.1 2.95 2.93 1.01
/rezept/show/713 4.99503 4.95098 200.2 202.0 5.00 4.95 1.01
/rezept/cat/Hauptspeise 5.89972 5.86025 169.5 170.6 5.90 5.86 1.01
/rezept/cat/Hauptspeise?page=5 6.00105 5.96334 166.6 167.7 6.00 5.96 1.01
/rezept/letter/G 5.95500 5.91001 167.9 169.2 5.95 5.91 1.01
This shows that pages which create new sessions receive around 35% speedup.
perf data file 1: 01-17.all.slow_components
requests=1000, options=-bm=all -mysql_session -fast_routes -fast_readers
-fast_connection -single_connection_cache_key -OT -lib=stablep
perf data file 2: 01-17.all.fast_components
requests=1000, options=-bm=all -mysql_session -fast_routes -fast_readers
-fast_connection -single_connection_cache_key -OT -lib=stablep2
page c1 real c2 real c1 r/s c2 r/s c1 ms/r c2 ms/r c1/c2
/empty/index 1.31928 1.25701 758.0 795.5 1.32 1.26 1.05
/welcome/index 1.49112 1.47056 670.6 680.0 1.49 1.47 1.01
/rezept/index 1.57464 1.55000 635.1 645.2 1.57 1.55 1.02
/rezept/myknzlpzl 1.57047 1.54025 636.8 649.2 1.57 1.54 1.02
/rezept/show/713 3.56991 3.52824 280.1 283.4 3.57 3.53 1.01
/rezept/cat/Hauptspeise 4.38857 4.34247 227.9 230.3 4.39 4.34 1.01
/rezept/cat/Hauptspeise?page=5 4.49025 4.44238 222.7 225.1 4.49 4.44 1.01
/rezept/letter/G 4.43585 4.38071 225.4 228.3 4.44 4.38 1.01
This shows that request processing is 5% faster than before (/empty/index). Action cached pages receive a 2% improvement. Even relativley complex pages retrieving around 30 objects from the database see a 1% improvement.