Change:
S := now.Sub(r.startTime).Seconds() // When a request finishes being served, and the actual service time was S, <del> // the queue’s virtual start time is decremented by G - S. r.queue.virtualStart -= (qs.estimatedServiceTime * float64(r.Seats())) - S </del> <ins> // the queue’s virtual start time is decremented by (G - S)*width. r.queue.virtualStart -= (qs.estimatedServiceTime - S) * float64(r.Seats()) </ins> // request has finished, remove from requests executing r.queue.requestsExecuting--

Discussion:
I reviewed the current code to see what had been done to generalize request width. I found a few inconsistencies in . A really simple one is at line , where is subtracted from the product but should have been subtracted from just (and their difference multiplied by ). With that fixed, a queue's tracks the expected "virtual time" ( in the original paper) when the oldest waiting request will start. However, computes the expected finish "virtual time" of the J'th request by multiplying by but not . There should be a consistent notion of "virtual time" (AKA ), either always including seats or excluding them. Kubernetes version (use ): latest k8s/apiserver sources Cloud provider or hardware configuration: OS (e.g: ): Kernel (e.g. ): Install tools: Network plugin and version (if this is a network-related bug): Others:
BTW, I suspect it would be better if "virtualTime" is not multiplied by request width. I am working on writing.
/triage accepted
I am running into a dilemma. The whole point of "virtual time" --- R(t) in the original paper --- is that it is shared among all the queues. They all get work done at the same rate, dR/dt. But when we generalize so that requests have different widths (and thus work is the sum over requests of ), even restricting the virtual world so that a queue runs only one request at a time does not save us from the fact that different queues get work done at different rates. Thus, even discarding fairness, and executing one request at a time per queue in the virtual world, it looks like what can be shared among queues is not a matter of work done.
But the current implementation approach makes the assumption that all the non-empty queues get work done at the same rate. As long as we are willing to tolerate the lossages due to that fiction, the queues can share one R(t) meter.
I see both sides of this; I still think it is double counting to use to advance virtual time AND to occupy W seats. OTOH, I also think it is right that we should be fair over the work () -- not just duration. One crazy idea is to add the request to multiple queues, and not execute it until it gets to the head of all the queues it is in.

The number of queues it needs to be to would be (# queues) * W / seats, rounded up I guess.
I think "virtual time" is a fuzzy concept that does not come equipped with guidelines about how to think about it correctly. That is why I followed the style of reasoning in the original paper, it defines a specific function and shows why and how that particular function is useful. In the KEP revision I am preparing, I think the problem statement ()the equations defining intended behavior ()the implementation discussion () make sense. It is in the last of those that is introduced, and I think it is clear that this should be a measure that includes request width.
I don't think it's double counting, because those are different things. And yes - we should be fair over WD - this is exactly why I wanted to push virtual time by WD. - I've seen your KEP PR, just I'm under water. My Fridays are real working time, so I will take a look tomorrow.