归位迁移改为sqlite三件套原子搬移,失败整组回滚
quest_data.db/bond.db/garbage.db/pvp.db 都是 WAL 模式 sqlite,运行时 -shm/-wal 与主库必须同进同退:单搬主库会丢未checkpoint事务导致数据回退, 单搬wal缺主库直接损坏。原实现逐文件rename,崩在中间会留下破损三件套。 改为每个 db 一组,主库/-shm/-wal 整体搬,任一失败立即把已搬的搬回原位; 目标端已存在则整组跳过不合并。创建时路径直接指向子目录(QuestStorage/ PlayerStorage 已改),迁移只兜存量。
This commit is contained in:
@@ -988,32 +988,31 @@ public class Main extends JavaPlugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 任务94:根目录5类文件归位(2026-10-08) =====
|
// ===== 任务94:根目录杂项归位(2026-10-08);#99 quest_data.db 归任务面板 =====
|
||||||
/**
|
/**
|
||||||
* 启动最早期把根目录杂项移入对应子文件夹(幂等)。
|
* 启动最早期把根目录杂项移入对应子文件夹(幂等)。
|
||||||
* 目标已存在则跳过不覆盖;迁移失败只警告不阻断启动。
|
* 目标已存在则跳过不覆盖;迁移失败只警告不阻断启动。
|
||||||
* 必须在任何 Manager 打开文件/数据库之前调用。
|
* 必须在任何 Manager 打开文件/数据库之前调用。
|
||||||
|
*
|
||||||
|
* ★ sqlite 三件套(.db/-shm/-wal)必须**整体**搬运:单搬 .db 会丢掉
|
||||||
|
* -wal 里尚未 checkpoint 的事务(数据回退),单搬 -wal/-shm 缺主库
|
||||||
|
* 直接损坏。故每组三件套作为一个事务处理,任一失败即整组回滚。
|
||||||
*/
|
*/
|
||||||
private void migrateRootDataFiles() {
|
private void migrateRootDataFiles() {
|
||||||
File root = getDataFolder();
|
File root = getDataFolder();
|
||||||
|
// {文件名, 目标子目录};sqlite 组以第三位 true 标记「三件套整体搬」
|
||||||
String[][] moves = {
|
String[][] moves = {
|
||||||
{"bond.db", "财务债券"},
|
{"bond.db", "财务债券", "1"},
|
||||||
{"bond.db-shm", "财务债券"},
|
{"chat.txt", "发言规则管控", "0"},
|
||||||
{"bond.db-wal", "财务债券"},
|
{"garbage.db", "垃圾箱", "1"},
|
||||||
{"chat.txt", "发言规则管控"},
|
{"pvp.db", "pvp", "1"},
|
||||||
{"garbage.db", "垃圾箱"},
|
{"maxsell_config.txt", "shop", "0"},
|
||||||
{"garbage.db-shm", "垃圾箱"},
|
|
||||||
{"garbage.db-wal", "垃圾箱"},
|
|
||||||
{"pvp.db", "pvp"},
|
|
||||||
{"pvp.db-shm", "pvp"},
|
|
||||||
{"pvp.db-wal", "pvp"},
|
|
||||||
{"maxsell_config.txt", "shop"},
|
|
||||||
// #99 quest_data.db 归入「任务面板」(任务进度数据)
|
// #99 quest_data.db 归入「任务面板」(任务进度数据)
|
||||||
{"quest_data.db", "任务面板"},
|
{"quest_data.db", "任务面板", "1"},
|
||||||
{"quest_data.db-shm", "任务面板"},
|
|
||||||
{"quest_data.db-wal", "任务面板"},
|
|
||||||
};
|
};
|
||||||
for (String[] m : moves) {
|
for (String[] m : moves) {
|
||||||
|
boolean group = "1".equals(m[2]);
|
||||||
|
// sqlite 组:主库不在根目录就整组跳过(已迁移或从未存在)
|
||||||
File src = new File(root, m[0]);
|
File src = new File(root, m[0]);
|
||||||
if (!src.exists()) {
|
if (!src.exists()) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1027,11 +1026,55 @@ public class Main extends JavaPlugin
|
|||||||
+ m[1] + "/" + m[0]);
|
+ m[1] + "/" + m[0]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (src.renameTo(dst)) {
|
if (!group) {
|
||||||
getLogger().info("[归位] " + m[0] + " → " + m[1] + "/");
|
// 普通单文件:直接搬
|
||||||
} else {
|
if (src.renameTo(dst)) {
|
||||||
getLogger().warning("[归位] " + m[0] + " → " + m[1]
|
getLogger().info("[归位] " + m[0] + " → " + m[1] + "/");
|
||||||
+ "/ 迁移失败(保留原位,请手动移动)");
|
} else {
|
||||||
|
getLogger().warning("[归位] " + m[0] + " → " + m[1]
|
||||||
|
+ "/ 迁移失败(保留原位,请手动移动)");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// sqlite 三件套:主库 + -shm + -wal 作为一个整体搬,任一失败整组回滚
|
||||||
|
String[] ext = {"", "-shm", "-wal"};
|
||||||
|
File[] from = new File[3];
|
||||||
|
File[] to = new File[3];
|
||||||
|
boolean anyMoved = false;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
from[i] = new File(root, m[0] + ext[i]);
|
||||||
|
to[i] = new File(dir, m[0] + ext[i]);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
if (!from[i].exists()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (to[i].exists()) {
|
||||||
|
getLogger().warning("[归位] " + m[0] + ext[i]
|
||||||
|
+ " 目标已存在,整组跳过(根目录保留副本): "
|
||||||
|
+ m[1] + "/" + m[0] + ext[i]);
|
||||||
|
// 目标存在 → 不能安全合并,整组不动(主库已在上面判过 dst 不存在,
|
||||||
|
// 走到这里只可能是 -shm/-wal 残留)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (from[i].renameTo(to[i])) {
|
||||||
|
anyMoved = true;
|
||||||
|
getLogger().info("[归位] " + m[0] + ext[i]
|
||||||
|
+ " → " + m[1] + "/");
|
||||||
|
} else {
|
||||||
|
getLogger().warning("[归位] " + m[0] + ext[i]
|
||||||
|
+ " → " + m[1] + "/ 迁移失败,整组回滚");
|
||||||
|
// 回滚:把本次已搬的搬回去,避免留下 db 与 wal 分离
|
||||||
|
for (int k = 0; k < 3; k++) {
|
||||||
|
if (to[k].exists() && !from[k].exists()) {
|
||||||
|
to[k].renameTo(from[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!anyMoved) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 用户点名的 maxshell_*.txt 也归入 shop/
|
// 用户点名的 maxshell_*.txt 也归入 shop/
|
||||||
|
|||||||
Reference in New Issue
Block a user