Skip to content

fix(session): 会话副本继承源会话时间戳,不再污染目标账号列表顺序 - #105

Closed
magicapple123 wants to merge 1 commit into
changexbc:mainfrom
magicapple123:fix/issue-103-copy-timestamps
Closed

magicapple123 wants to merge 1 commit into
changexbc:mainfrom
magicapple123:fix/issue-103-copy-timestamps

Conversation

@magicapple123

Copy link
Copy Markdown
Contributor

背景与根因

#103:把账号 A 的会话复制到账号 B,目标账号里批量复制的历史会话会被顶到列表最上方,把用户正在进行的对话挤下去,看起来像原有对话被覆盖或丢失。

根因在 insert_session_copy:写入副本时 created_at / updated_at 无条件赋值为 now_ms(),而客户端会话列表按这两个字段降序排列。

同一行数据的 last_activity_at 是原样继承的(走的是 _ => vals.push(v) 分支),说明源会话的原始时间信息在复制流程里完全可得,只是没写进排序依赖的列。

改动

insert_session_copy 中 created_at / updated_at 改为继承源行的值;源值为空(脏数据)时才回退到复制时刻,保证排序列始终可比较。

新增回归测试 insert_session_copy_inherits_source_timestamps,覆盖两条路径:正常继承(1000 / 2000)与空值回退(落在复制时刻区间内)。

没做的

冲突提示

本 PR 只改 insert_session_copy 内部 6 行 + 新增一个测试(约 5150 行处)。同样触碰 session.rs 的 #62 / #66 / #68 / #69 改的是备份可见性、清理逻辑等其它位置,与本改动不相邻,预期可自动合并。

验证

  • cargo test -p wb-switch-core:607 passed / 0 failed(含新增测试)
  • cargo fmt --check:本改动行无格式差异(上游本身有 87 处既有 diff,未触碰)

closes #103

复制会话时 created_at / updated_at 曾一律写成复制时刻,而客户端会话列表
按这两个字段降序排列 —— 批量复制的历史会话会被顶到列表最上方,把用户正在
进行的对话挤下去,看起来像原有对话被覆盖或丢失。

改为继承源行的原始时间戳(last_activity_at 本来就沿用原值,说明原时间可得);
源值为空时才回退到复制时刻,保证排序列始终可比较。

新增回归测试 insert_session_copy_inherits_source_timestamps。

closes changexbc#103
@magicapple123

Copy link
Copy Markdown
Contributor Author

@changexbc 也麻烦帮忙看下这个修复 🙏

#103 的根因很小:insert_session_copy 写副本时把 created_at/updated_at 无条件置为当前时间,客户端列表按时间倒序排,副本就被顶到最上面——看起来像原会话被覆盖。修复为继承源会话时间戳(脏数据才回退),只动 6 行 + 一个回归测试,与同样触碰 session.rs 的在途 PR 改动位置不相邻,可自动合并。

三平台 CI 全绿,有需要调整的随时提,谢谢!

@changexbc

Copy link
Copy Markdown
Owner

@changexbc 也麻烦帮忙看下这个修复 🙏

#103 的根因很小:insert_session_copy 写副本时把 created_at/updated_at 无条件置为当前时间,客户端列表按时间倒序排,副本就被顶到最上面——看起来像原会话被覆盖。修复为继承源会话时间戳(脏数据才回退),只动 6 行 + 一个回归测试,与同样触碰 session.rs 的在途 PR 改动位置不相邻,可自动合并。

三平台 CI 全绿,有需要调整的随时提,谢谢!

感谢,这个我晚上具体看下吧,这个是 bug 吗,还是如何排序的问题?我们的这个关联会话我测过没问题啊,如果只是一直复制的会话在最前面我觉得不算是问题。

@magicapple123

Copy link
Copy Markdown
Contributor Author

澄清一下,这个和第 0.1.41 起的关联会话机制是两件事——你测的「反复切号不会越复制越多」确实没问题,这个 PR 不碰那部分。

现象(#103 里报的):把 A 账号的会话复制到 B 账号后,在 B 账号的客户端里,这批副本会全部出现在列表最上方,把 B 账号自己正在进行的对话挤到下面,看起来像「原有会话被覆盖或丢失」。

根因在 insert_session_copy(session.rs:538-544):

match col.as_str() {
    "id" => ...new_cid,
    "user_id" => ...target_uid,
    "created_at" | "updated_at" => vals.push(Integer(now_ms())),   // ← 无条件写复制时刻
    "deleted_at" => vals.push(Null),
    _ => vals.push(v),                                              // ← last_activity_at 等原样继承
}

客户端会话列表按 created_at / updated_at 降序排,副本被写成「刚刚发生」就必然插队到最前。而同一行数据的 last_activity_at 反而是原样继承源会话的——说明源会话的原始时间在复制流程里完全可得,只是没写进排序依赖的那两列。

修复:created_at / updated_at 改为继承源行的值(源值为空这种脏数据才回退到复制时刻,保证排序列始终可比较)。效果是副本在 B 账号列表里的位置与它在 A 账号原始时间轴上一致,不再插队。

如果你的设计意图就是「刚复制进来的会话要排最前、方便一眼看到」,那这个 PR 可以直接关掉,你定就行 🙏 如果认同副本应保留源时间语义,这个 PR 就是那 6 行的改动 + 一个回归测试。

@changexbc

Copy link
Copy Markdown
Owner

你的意思是复制的绘画会出现在列表会话列表的最前面对吗?我理解的是复制的绘画是即将要继续进行的绘画,没什么问题吧。

@magicapple123

Copy link
Copy Markdown
Contributor Author

嗯,我理解你的立场——副本是「接下来要继续进行的会话」,放前面方便一眼找到,这个意图完全合理。

我想补一个数据一致性的角度供你判断,问题可能不在于「副本该排前还是排后」:

复制后的行里,两个时间字段会互相矛盾——

  • created_at / updated_at:复制时刻(例如今天 14:30)
  • last_activity_at:原样继承源会话(例如源对话实际发生在 5 天前)

客户端按 created_at/updated_at 排 → 副本在最前(符合你的意图 ✅);但任何按「最近活跃」语义读这行的地方(列表时间标签、其它排序、后续会话整理)拿到的却是 5 天前。同一行两个字段给出相反结论,这就是 #103 里用户体感「对话像丢了/被覆盖」的来源——源账号里明明是几天前的对话,复制过来却显示「刚刚」。

所以两个方向,你定就好:

  1. 保持现状(副本 = 新时间,进列表最前):那这个 PR 直接关掉,我去 [Bug] 会话复制到已存在同内容会话的账号时未作幂等去重,且改写 created_at 导致列表顺序混乱 #103 里跟报问题的朋友解释这是有意的设计选择;
  2. 只做一致性:created_at / updated_at 改为继承源值,整行时间语义统一,副本落回它原本的时间位置——「即将继续进行的会话」用别的机制表达(比如复制后直接打开、或加置顶/标记)语义更准。

两种我都接受 🙏

@magicapple123

Copy link
Copy Markdown
Contributor Author

我又做了更硬的实证,结论反过来了:这个 PR 应该关掉,我上一轮的推断有误。

我直接在 WorkBuddy 客户端产物(resources/app.asar)里查了会话列表的 SQL,两种 transport 用的是同一条排序:

-- cloud 活跃 / 归档列表、local 列表,共 4 处
ORDER BY COALESCE(last_activity_at, created_at) DESC, id ASC

配套的还有客户端建在库上的索引(同一意图):

CREATE INDEX idx_sessions_cloud_sort ON sessions(user_id, last_activity_at, created_at)
  WHERE transport = 'cloud' AND deleted_at = -1

关键在 COALESCE 的语义:优先取 last_activity_at,只有它为 NULL 时才回退到 created_at。副本的 last_activity_at 是原样继承源会话的(正如 #103 里观察到的),所以副本在列表里落在它原本的时间位置,不会插队;created_at / updated_at 根本不参与这个排序。

我上一条说的「同一行两个时间字段矛盾」在数据本身层面确实成立,但既然它不进排序、也没有其它读取路径依赖它,就没有用户可见影响——不是 bug。你的判断是对的 🙏

补充一个可能有用的信息:会看到「副本排最前」的地方是本工具自己的会话列表(session.rs 里 list_sessions_for_user_at 用的是 ORDER BY updated_at DESC)。如果希望它跟客户端保持一致(COALESCE(last_activity_at, created_at)),我可以另开一个小 PR 只改那一处排序;如果觉得那个列表按复制时间排更符合工具场景,就这样放着。

先关掉这个 PR,感谢花时间看 ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] 会话复制到已存在同内容会话的账号时未作幂等去重,且改写 created_at 导致列表顺序混乱

2 participants