eb7e6ccb9b23c2dd5cd82de0237c6f25aecd2da1
max
  Thu Sep 17 06:02:11 2026 -0700
Hub tracks must not claim an undecorated name that a native track owns

#Preview2 week - bugs introduced now will need a build patch to fix
A hub track can be named on a URL, or found in the cart, without its "hub_<id>_"
prefix, so that hub links stay readable.  Nothing checked whether the assembly
already had a track of that name, so when it did, the hub track took the
variable: with the MethBase2 hub attached, setting the native hg38 Methbase
track's visibility moved it to hub_<id>_Methbase and dropped it from the native
track, which then did not draw.  The two share the name "Methbase", and their
subtracks share names too, so the same went for the _sel checkboxes.

hubTrackOwnsBareName() now gates the seven places that fall back to the bare
name - track and subtrack visibility, supertrack visibility, the two _hideKids
variables, and the _sel checkbox.  The bare name is the hub track's only where
the assembly has no track of that name; otherwise it belongs to the native one,
whose own pass over the track list consumes it.

It answers from the track list loadFromTrackDb() has already built, so it costs
a hash lookup: hub tracks are always prefixed in that list, so an undecorated
name can only match a native track.  A caller with no list to register (hgTrackUi)
falls back to a single-row trackDb query per distinct bare name, cached.  With
500 hub subtracks visible and 500 bare names on the URL - far past anything real
- that is 189 queries either way, against 1189 for a query per name.

Also clarifies the comments on hTrackDbWithCartVersion() and tdbForTrack(): the
"this result is cached" note means the shared-memory trackDb cache only, which
is off unless cacheTrackDbDir is set, and there is no memoizing besides it - so
hTrackDbForTrack() per name reloads the whole trackDb each call.  Measured at 57
seconds for 500 names before I abandoned that approach.

refs #34246

diff --git src/hg/inc/hdb.h src/hg/inc/hdb.h
index 7a1ae0dce09..f5092403dfa 100644
--- src/hg/inc/hdb.h
+++ src/hg/inc/hdb.h
@@ -569,31 +569,36 @@
  * the supertrack trackDb subtrack fields are not set here (would be
  * incompatible with the returned list)
  * Returns list sorted by priority
  *	NOTE: this result is cached, do not free it !
  */
 
 struct trackDb *hTrackDbWithCartVersion(char *db, int *retCartVersion);
 /* Do hTrackDb AND return cartVersion. */
 
 struct trackDb *tdbForTrack(char *db, char *track,struct trackDb **tdbList);
 /* Load trackDb object for a track. If track is composite, its subtracks
  * will also be loaded and inheritance will be handled; if track is a
  * subtrack then inheritance will be handled.  (Unless a subtrack has
  * "noInherit on"...) This will die if the current database does not have
  * a trackDb, but will return NULL if track is not found.
- * MAY pass in prepopulated trackDb list, or may receive the trackDb list as an inout. */
+ * MAY pass in prepopulated trackDb list, or may receive the trackDb list as an inout.
+ *   Pass tdbList whenever you look up more than one track.  With it NULL this loads the
+ * whole trackDb to find one name, and that is only cheap where the shared-memory cache
+ * is on (cacheTrackDbDir in hg.conf) - with the cache off hTrackDb() does not memoize,
+ * so a loop over N names is N full trackDb loads.  To ask only whether a name exists,
+ * hMaybeTrackInfo() is a single-row query instead. */
 #define hTrackDbForTrack(db,track) tdbForTrack(db,track,NULL)
 
 struct trackDb *hTrackDbForTrackAndAncestors(char *db, char *track);
 /* Load trackDb object for a track. If need be grab its ancestors too.
  * This does not load children. hTrackDbForTrack will handle children, and
  * is actually faster if being called on lots of tracks.  This function
  * though is faster on one or two tracks. */
 // WARNING: this works for hub and db tracks but not custom tracks.
 
 struct trackDb *hCompositeTrackDbForSubtrack(char *db, struct trackDb *sTdb);
 /* Given a trackDb that may be for a subtrack of a composite track,
  * return the trackDb for the composite track if we can find it, else NULL.
  * Note: if the composite trackDb is found and returned, then its subtracks
  * member will contain a newly allocated tdb like sTdb (but not ==). */