[ic] A brief discussion of UserTags and their presence in RPC server daemons

Chris Keane chris.keane at zzgi.com
Sat Jul 21 14:47:02 UTC 2012

Imagine this case:

* A single interchange instance with a comprehensive set of system-wide 
custom UserTags
* Multiple catalogs within the interchange instance.
    Some of them have UserTags overriding some of the system-wide 
UserTags with catalog-level tags.
    All catalogs have at least a few tags named the same that implement 
catalog-specific logic.

Problem: inconsistency in which version of a UserTag is actually called:

In cases where the global UserTag is not overridden by any local tags, 
the global tag is properly called (good).

In instances where a local UserTag in one catalog overrides a global 
UserTag, sometimes the tag is also overridden in all catalogs, sometimes 
not (bad)

In instances where multiple catalogs have the same local UserTag, the 
actual UserTag called in any catalog is a random* selection of those 
UserTags from all catalogs, i.e. it's not necessarily the UserTag from 
the actual calling catalog, it might be one from a completely separate 
catalog. (bad)

*random: it's not really random, its actually the version from the first 
catalog that was called in each server Child process.

The reason:

Within each server Child process global UserTags are loaded into the 
Parse space (into %Routines)
They are then supplemented/overridden by local UserTags. However, if 
even a single catalog overrides the in-memory copy of a global UserTag 
in $Routines{tagname} it will be overridden for all subsequent requests 
served by that server Child. Even for catalogs which are not supposed to 
be overriding it.
Once a server Child process starts and receives its first request, it 
loads all the local UserTags for that first catalog called, but does not 
do so again. Result: that first catalog's local UserTags being used by 
all subsequent calls to that Child regardless of which catalog they are for.

The most obvious and braindead solution is to update the RPC mode 
settings to have MaxRequestsPerChild equal 1. In this setting, each 
Child will handle one request, die, and spawn a new one, thereby freshly 
setting all UserTags for one specific catalog call. However, there is 
then little advantage to using RPC mode, and in our case where we send 
hundreds of tiny ajax requests through the interchange server, would 
require quite a high StartServers number.

Alternately, fix the code. We gone through various iterations of this 
over the last few years. Right now we're sitting with:
in Vend::Parse
in new()
                   unless $Vend::Tags_added++;
         add_tags($Global::UserTag);  # CK added because later-loaded 
Usertags from different catalogs were overriding global tags if
                                         # some other catalog had a 
local version of the tag but this one relied on the global version

Note, the conditional Tags_added removed to force load of tags every 
time. Otherwise, stale versions from other catalogs will be used instead 
of the ones from our catalog.
Note, added add_tags($Global::UserTag) to force a fresh copy of the 
global usertags in case a previous call within this Child to a different 
catalog had overridden the global tag with its own, but we don't 
override it in this catalog.

The downside of performing these add_tags is performance. We don't have 
any benchmarks. sub add_tags is a memory-only operation so not as 
terrible as it could be. But basically it's additional stuff that needs 
to happen before we even start real processing which we're trying to 
optimize out wherever possible.

The next step to making this better will be to rewrite Parse::do_tag to 
remove the use of the %Routine hash altogether. Basically, at time of 
execution try finding $Vend::Cfg->{UserTag}->{Routine} first, then try 
$Global::UserTag->{Routine} next if not found.

Thoughts, suggestions?


More information about the interchange-users mailing list