Order override writes per model
Saving twice quickly, or saving while the one-time backfill is still running, started independent requests with no sequencing, so the older response could commit last and resurrect the entry the newer one meant to replace or remove. An API-driven load then applies context or GPU settings the user has already changed, with nothing in the UI showing it. Writes now chain per override key. The chain hangs off the settled tail, so a failed write cannot cancel the next one, and only the last writer clears the slot so a queue that is still building keeps its order. Different models still overlap. Verified against the real module under node: two saves for one model with the first made slow commit oldest-first and never overlap, where the previous version committed them in the wrong order; a rejected write still lets the next succeed; and two models still run concurrently.
This commit is contained in:
parent
221b810d52
commit
423532cbcb
2 changed files with 48 additions and 0 deletions
|
|
@ -123,10 +123,41 @@ function toApiOverride(config: PerModelConfig | null): ApiModelOverride {
|
|||
return payload;
|
||||
}
|
||||
|
||||
// One in-flight write per model, so writes for the same model commit in the
|
||||
// order they were issued. Saving twice quickly, or saving while the one-time
|
||||
// backfill is still running, otherwise leaves two independent requests racing:
|
||||
// the older response can land last and resurrect the entry the newer one meant
|
||||
// to replace or remove, and an API load then applies settings the user has
|
||||
// already changed. Different models still overlap.
|
||||
const writesByKey = new Map<string, Promise<void>>();
|
||||
|
||||
export async function putModelOverride(
|
||||
modelId: string,
|
||||
ggufVariant: string | null | undefined,
|
||||
config: PerModelConfig | null,
|
||||
): Promise<void> {
|
||||
const key = modelOverrideKey(modelId, ggufVariant);
|
||||
// Chain on the settled tail: a failed write must not cancel the next one.
|
||||
const previous = writesByKey.get(key) ?? Promise.resolve();
|
||||
const write = previous
|
||||
.catch(() => {})
|
||||
.then(() => sendModelOverride(modelId, ggufVariant, config));
|
||||
writesByKey.set(key, write);
|
||||
try {
|
||||
await write;
|
||||
} finally {
|
||||
// Only the last writer clears the slot, so a queue that is still building
|
||||
// keeps its ordering.
|
||||
if (writesByKey.get(key) === write) {
|
||||
writesByKey.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function sendModelOverride(
|
||||
modelId: string,
|
||||
ggufVariant: string | null | undefined,
|
||||
config: PerModelConfig | null,
|
||||
): Promise<void> {
|
||||
const res = await authFetch(OVERRIDES_URL, {
|
||||
method: "PUT",
|
||||
|
|
|
|||
|
|
@ -763,3 +763,20 @@ def test_monitor_overlay_does_not_pull_in_the_lazy_page():
|
|||
shared = _read("features/api-monitor/lifecycle.ts")
|
||||
assert "export function isLifecycleEntry(" in shared
|
||||
assert "export function lifecycleLabel(" in shared
|
||||
|
||||
|
||||
def test_override_writes_are_ordered_per_model():
|
||||
"""Two saves for one model, or a save racing the one-time backfill, started
|
||||
independent requests with no sequencing, so the older response could commit
|
||||
last and resurrect the entry the newer one meant to replace. An API load
|
||||
then applies settings the user has already changed. Different models still
|
||||
overlap, so a slow write for one cannot hold up another.
|
||||
"""
|
||||
src = " ".join(_read("features/model-picker/api/model-overrides.ts").split())
|
||||
assert "const writesByKey = new Map<string, Promise<void>>();" in src
|
||||
# Keyed by the same override key the server stores under.
|
||||
assert "const key = modelOverrideKey(modelId, ggufVariant);" in src
|
||||
# Chained on the settled tail, so one failed write cannot cancel the next.
|
||||
assert "previous .catch(() => {}) .then(() => sendModelOverride(" in src
|
||||
# Only the last writer clears the slot, or a queue still building loses order.
|
||||
assert "if (writesByKey.get(key) === write) { writesByKey.delete(key); }" in src
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue