mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-15 08:02:53 +00:00
fix: keep an edit this read's own retry got refused
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
112d95ba5e
commit
d902638c71
@@ -412,6 +412,7 @@ class Entry<V> {
|
||||
let rowsAtRead = 0
|
||||
let valuesAtRead = 0
|
||||
let unbasedPending = false
|
||||
let refusedBefore = false
|
||||
let waitingBefore: unknown | undefined
|
||||
let res: ItemLoad<V>
|
||||
try {
|
||||
@@ -423,6 +424,8 @@ class Entry<V> {
|
||||
// would go out unconditional and take over a row created since. The read below
|
||||
// gives it one, and `pending` keeps showing it meanwhile.
|
||||
unbasedPending = this.ports.unbased(key)
|
||||
// Whether the server had already refused this key before this read went near it.
|
||||
refusedBefore = this.ports.conflicted(key)
|
||||
if (!unbasedPending) await this.ports.flush(key)
|
||||
// Taken here, not when the command was asked for: rows this read's own flush sent
|
||||
// are in the response it is about to get. Only one handed over from now on, while
|
||||
@@ -472,7 +475,10 @@ class Entry<V> {
|
||||
// A row the syncer took but could not send is newer than this response and is the only
|
||||
// copy of that edit, so the item opens on it. One the server *refused* is the other
|
||||
// way round — the response is what won — and `adoptRow` drops it below.
|
||||
const unsent = this.ports.conflicted(key) ? undefined : waitingBefore
|
||||
// A payload the server refused *before* this read is stale: the response is what won.
|
||||
// One refused by this read's own retry is not — it is the user's unsent edit, and the
|
||||
// only copy of it, so it stays on screen with the conflict for them to settle.
|
||||
const unsent = refusedBefore ? undefined : waitingBefore
|
||||
// A parked delete is what this tab wants gone, not what is there: the row is still the
|
||||
// server's, so the reconcile at the end re-issues the delete instead of believing it
|
||||
// landed and leaving the draft behind with nothing to remove it.
|
||||
|
||||
@@ -2051,6 +2051,40 @@ describe('item store: conflicts', () => {
|
||||
expect(rows.sent.get('u/me/r')).toBeNull()
|
||||
})
|
||||
|
||||
it('keeps the failed edit its own reopening retry got refused', async () => {
|
||||
const rows = fakeRows()
|
||||
const store = createItemStore(rows.port)
|
||||
const key: ItemKey = { workspace: 'w', kind: 'resource', path: 'u/me/r' }
|
||||
const a = adapter(async () => ({
|
||||
deployed: deployedRes,
|
||||
draft: rows.sent.get('u/me/r') as Res | undefined,
|
||||
draftSavedAt: 'T1'
|
||||
}))
|
||||
// A draft exists, so this key has a baseline and its autosave is not unconditional.
|
||||
rows.sent.set('u/me/r', { ...deployedRes, description: 'the first draft' })
|
||||
const first = store.acquire(key, { workspace: 'w', path: 'u/me/r' }, a)
|
||||
await settle()
|
||||
rows.failing.add('u/me/r')
|
||||
const mine = { ...deployedRes, description: 'mine, never sent' }
|
||||
first.handle.value = mine
|
||||
await rows.port.flush(key)
|
||||
first.release()
|
||||
|
||||
// Another tab writes a newer draft, and this tab's network recovers. Reopening retries
|
||||
// the parked edit before reading, and that retry is what the server refuses.
|
||||
rows.sent.set('u/me/r', { ...deployedRes, description: 'the other tab' })
|
||||
rows.failing.delete('u/me/r')
|
||||
rows.conflictOnFlush.add('u/me/r')
|
||||
|
||||
const second = store.acquire(key, { workspace: 'w', path: 'u/me/r' }, a)
|
||||
await settle()
|
||||
|
||||
// That edit is the only copy anyone has. Dropping it for the remote draft and clearing
|
||||
// the conflict on the way past would lose it with nothing said.
|
||||
expect(second.handle.value).toEqual(mine)
|
||||
expect(second.handle.status).toBe('conflicted')
|
||||
})
|
||||
|
||||
it('opens a conflicted item afresh, past the payload the server refused', async () => {
|
||||
const rows = fakeRows()
|
||||
const store = createItemStore(rows.port)
|
||||
|
||||
Reference in New Issue
Block a user