So i think i've found a monster...while going spelunking this way and that trying to track down why this experiment is not loading correctly in QA, I found that findOneExperiment called in /experiment/:id was to blame because within it we call createBaseQueryBuilder which does one query with exponentially more expensive joins the more decision points, conditions, and segments are involved.
Then looking around I see that we also use createBaseQueryBuilder in csv export and /batch-assign`.... it's not quite as bad per experiment fetch because it is only for one decision point which eliminates one factor, but still not good and it's for every valid experiment in the context.
Claude helped me break this down into something concrete I can understand: The formula is essentially as shown below, this would be the # of rows the query ends up creating.
- D = number of decision points
- C = number of conditions
- I and E = number of segments in inclusion list and exclusion lists one row deep (aka not every nested segment in this particular call, just the root segments to be resolved, but that's not much of a comfort based on discussions we've had about
getSegmentById issues. Let's leave that out of this but assume it's making things slower also).
when cache is missed and hits the db:
- /experiment/:id will be (C² × D² × I × E) times slower than it should be
- data-export and /batch-assign) will be (C² × D × I × E) times slower than it should be (for every experiment fetched)
so for express mode, we would be getting (3^2 * 46^2) = 19044 rows at least, multiplied by roughly number of segments involved also, which is almost entirely thrown away by the logic.
in my testing, I am trying to split up these into paralell calls, it is making a gigantic difference, it becomes as quick as just (C x D) (or roughly I x E), which is (138 rows). Everything goes so much faster, including running tests.
So i think i've found a monster...while going spelunking this way and that trying to track down why this experiment is not loading correctly in QA, I found that
findOneExperimentcalled in/experiment/:idwas to blame because within it we callcreateBaseQueryBuilderwhich does one query with exponentially more expensive joins the more decision points, conditions, and segments are involved.Then looking around I see that we also use
createBaseQueryBuilderin csv export and /batch-assign`.... it's not quite as bad per experiment fetch because it is only for one decision point which eliminates one factor, but still not good and it's for every valid experiment in the context.Claude helped me break this down into something concrete I can understand: The formula is essentially as shown below, this would be the # of rows the query ends up creating.
getSegmentByIdissues. Let's leave that out of this but assume it's making things slower also).when cache is missed and hits the db:
so for express mode, we would be getting (3^2 * 46^2) = 19044 rows at least, multiplied by roughly number of segments involved also, which is almost entirely thrown away by the logic.
in my testing, I am trying to split up these into paralell calls, it is making a gigantic difference, it becomes as quick as just (C x D) (or roughly I x E), which is (138 rows). Everything goes so much faster, including running tests.