Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

Offline action reports success after leadership loss before outbox admission

未关闭
#1,939 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
50/100
Issue 类型
缺陷
描述清晰度
基本清楚
活跃度
活跃
技术栈
typescript

调研方向

Start with packages/offline-transactions/tests/leadership-replay.property.test.ts and run the provided Vitest probe command. Trace persistTransaction() through its leadership guard and resolveTransaction(), then compare the public transaction settlement contract with the offline admission behavior. Extend coverage for selection-to-admission leadership loss, update docs/contributing/oracle-coverage.md and the guide from #1938, and verify the no-success-without-durability case.

由索引模型根据 Issue 内容生成。

描述

Bug

An offline action can report success after it loses leadership but before its mutation reaches the outbox. The mutation is neither durable nor sent to the server. In the controlled reproduction below, the Collection row is also absent after the success signal.

This was found while reviewing the documentation in #1938. The production code is unchanged by that docs PR. It is related to the offline admission policy tracked in #1659, but this is a concrete current-path failure.

Reproduction

On commit 9bf2444072c3c4aefebd072efd08f14be99afb0a, save this as packages/offline-transactions/tests/leadership-admission-review-probe.test.ts:

import { expect, it } from 'vitest'
import { createTestOfflineEnvironment } from './harness'

it('does not report success when leadership is lost before outbox admission', async () => {
  const env = createTestOfflineEnvironment()
  await env.waitForLeader()

  const action = env.executor.createOfflineAction({
    mutationFnName: env.mutationFnName,
    onMutate: (item: {
      id: string
      value: string
      completed: boolean
      updatedAt: Date
    }) => {
      env.collection.insert(item)
      env.leader.setLeader(false)
    },
  })

  try {
    const transaction = action({
      id: 'handoff-item',
      value: 'Buy milk',
      completed: false,
      updatedAt: new Date(),
    })
    const outcome = await transaction.isPersisted.promise.then(
      () => 'fulfilled',
      () => 'rejected',
    )
    const durable = await env.executor.peekOutbox()

    expect(env.executor.isOfflineEnabled).toBe(false)
    expect(env.collection.get('handoff-item')).toBeUndefined()
    expect([outcome, durable.length, env.mutationCalls.length]).not.toEqual([
      'fulfilled',
      0,
      0,
    ])
  } finally {
    env.executor.dispose()
    await env.collection.cleanup()
  }
})

Run pnpm --filter @tanstack/offline-transactions exec vitest run tests/leadership-admission-review-probe.test.ts. The test fails with:

AssertionError: expected [ 'fulfilled', +0, +0 ] to not deeply equal [ 'fulfilled', +0, +0 ]

The leadership change happens synchronously in onMutate, after createOfflineAction() has selected its offline wrapper. persistTransaction() reaches its guard after an await and calls resolveTransaction() when leadership is gone. It never calls outbox.add() or the configured mutationFn. The completion promise then fulfills.

Expected behavior and coverage

An action must not report success when no durable record exists and no server mutation ran. The admission policy needs a decision: either reject and roll back the action at this boundary, or hand it off durably to the next leader. The first option appears smaller, but both should be checked against the public transaction settlement contract.

packages/offline-transactions/tests/leadership-replay.property.test.ts is the primary leadership oracle. It covers already durable replay but does not generate the selection-to-admission leadership loss. Extend its grammar and public observations to cover that transition and nearby legal histories; prove the witness fails on current code and passes with the repair. Record the resulting scope in docs/contributing/oracle-coverage.md.

The guide in #1938 says actions use the online-only path when offline mode is disabled. That describes actions that start while it is disabled, but not this action, which selected the offline path before leadership changed. Update the guide alongside the behavior decision.

主要语言
TypeScript
星标
3.9k
派生
267
平均合并
1 天 7 小时
30 天内合并 PR
104

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

TanStack/db 的其他 Issue

查看 TanStack/db 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。