Skip to content

fix: 修复了当 log 名不存在时,使用指令会出现假 log 的情况#1584

Draft
kenichiLyon wants to merge 7 commits intosealdice:masterfrom
kenichiLyon:fix-log-id-0
Draft

fix: 修复了当 log 名不存在时,使用指令会出现假 log 的情况#1584
kenichiLyon wants to merge 7 commits intosealdice:masterfrom
kenichiLyon:fix-log-id-0

Conversation

@kenichiLyon
Copy link
Contributor

@kenichiLyon kenichiLyon commented Feb 8, 2026

fix #1582

目前找到问题所在为:

  1. log new时先构造了 newLogItem,导致 logID==0 时日志项被写入 log_id=0;随后读取不存在日志时继续用 logID=0 查询,读回了这些“假log”
  2. 读取路径在获取 logID 时,如果不存在会返回 0 且不报错,随后用 log_id=0 去查行数据

本 pr 的解决方案对应为:

  1. 修改构造流程,为 newLogItem.LogID 赋值为 真实的 logid 再记录消息
  2. 个人推断问题 2 是设计使然,而其影响实际在用户升级时会体现,修复问题 1 后自然就不存在 log_id=0 的假 log,那么也就不存在问题 2 。为了保证兼容性与问题 2 可能的历史包袱,我因此加入了 migration 进行清理,预计放入 v160,这里可讨论

已自行测试,图中test-bot为此 commit 后编译的海豹,流明则是此 commit 前

image

@sourcery-ai
Copy link

sourcery-ai bot commented Feb 8, 2026

Reviewer's Guide

Ensures log operations no longer create or read from fake logs with log_id=0 and adds a migration to clean up existing bad data.

Sequence diagram for updated log append and read behavior with logID zero handling

sequenceDiagram
    actor User
    participant Service as LogService
    participant DB as LogDB

    %% LogAppend with proper logID assignment
    User->>Service: LogAppend(groupID, logName, message)
    Service->>DB: Query LogInfo by groupID, logName
    alt existing log
        DB-->>Service: logID
    else new log
        Service->>DB: Create LogInfo
        DB-->>Service: newLog.ID as logID
    end
    Service->>Service: newLogItem.LogID = logID
    Service->>DB: Create LogOneItem(newLogItem)
    DB-->>Service: success
    Service-->>User: append success

    %% Read operations now guard against logID == 0
    User->>Service: LogGetAllLines(groupID, missingLogName)
    Service->>DB: Lookup logID by groupID, logName
    DB-->>Service: logID = 0
    alt logID is zero
        Service-->>User: return empty LogOneItem list
    else
        Service->>DB: Query LogOneItem by logID
        DB-->>Service: items
        Service-->>User: items
    end

    %% Delete by MsgID also guards against logID == 0
    User->>Service: LogMarkDeleteByMsgID(groupID, missingLogName, rawMsgID)
    Service->>DB: Lookup logID by groupID, logName
    DB-->>Service: logID = 0
    alt logID is zero
        Service-->>User: no op, return nil
    else
        Service->>DB: Delete LogOneItem where log_id = logID and raw_msg_id = rawMsgID
        DB-->>Service: success
        Service-->>User: delete success
    end
Loading

Class diagram for V160 logID zero cleanup migration integration

classDiagram
    class Upgrade {
        +string ID
        +string Description
        +func Apply(logf, operator) error
    }

    class V160LogIDZeroCleanMigrationHolder {
        +Upgrade V160LogIDZeroCleanMigration
        +func V160LogIDZeroCleanMigrate(dboperator, logf) error
    }

    class DatabaseOperator {
        +func GetLogDB(mode) LogDB
    }

    class LogDB {
        +func Model(value) LogDB
        +func Where(query, args) LogDB
        +func Pluck(column, dest) error
        +func Delete(value) DeleteResult
    }

    class DeleteResult {
        +int64 RowsAffected
        +error Error
    }

    class LogOneItem {
        +uint64 ID
        +uint64 LogID
    }

    class LogInfo {
        +uint64 ID
    }

    V160LogIDZeroCleanMigrationHolder "1" o-- "1" Upgrade : wraps
    V160LogIDZeroCleanMigrationHolder ..> DatabaseOperator : uses
    DatabaseOperator ..> LogDB : returns
    LogDB ..> LogOneItem : queries
    LogDB ..> LogInfo : queries
    LogDB ..> DeleteResult : returns
Loading

Flow diagram for V160 migration cleaning log_id zero data

flowchart TD
    A[Start V160LogIDZeroCleanMigration.Apply] --> B[Get LogDB with constant.WRITE]
    B --> C[Query LogOneItem where log_id = 0 and pluck id into itemIDs]
    C --> D[Delete from LogOneItem where log_id = 0]
    D --> E{itemIDs length > 0?}
    E -->|Yes| F[logf: 数据修复 - LogItems表,删除记录<br/>含 RowsAffected 和 itemIDs]
    E -->|No| G[logf: 数据修复 - LogItems表,没有需要删除的记录]

    F --> H[Query LogInfo where id = 0 and pluck id into logIDs]
    G --> H

    H --> I[Delete from LogInfo where id = 0]
    I --> J{logIDs length > 0?}
    J -->|Yes| K[logf: 数据修复 - Logs表,删除记录<br/>含 RowsAffected 和 logIDs]
    J -->|No| L[logf: 数据修复 - Logs表,没有需要删除的记录]

    K --> M[Return nil]
    L --> M[Return nil]
Loading

File-Level Changes

Change Details Files
Guard all log read/write paths against non-existent logs by treating logID=0 as "no log" instead of a valid ID.
  • Return empty result sets early in log line listing functions when resolved logID is 0 to avoid querying fake log entries.
  • Short‑circuit log deletion by message ID when resolved logID is 0 so no operations are attempted against invalid log IDs.
  • Ensure new log items are created with the real logID from LogInfo, assigning LogID only after the log row has been created or retrieved.
dice/service/log.go
Introduce a DB migration for v160 to clean up historical log records that used log_id=0 and register it in the upgrader.
  • Register the new v160 migration in the upgrade manager so it runs during initialization.
  • Implement V160LogIDZeroCleanMigrate to delete all LogOneItem rows with log_id=0 and LogInfo rows with id=0, logging what was removed.
  • Expose V160LogIDZeroCleanMigration as an upgrade.Upgrade describing and wrapping the cleanup process.
migrate/v2/enter.go
migrate/v2/v160/v160_logid0.go

Assessment against linked issues

Issue Objective Addressed Explanation
#1582 When querying a non-existent log via log get (including cursor/page variants), do not return any fake or dirty data associated with log_id = 0 (treat missing logs as having no lines).
#1582 Prevent new log entries from ever being written with log_id = 0 when creating/appending logs.
#1582 Clean up existing incorrect log data where log_id = 0 so that historical dirty data is removed.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • In V160LogIDZeroCleanMigrate, collecting all matching IDs into slices just for logging can be expensive on large datasets; consider logging only RowsAffected or limiting/plucking IDs conditionally (e.g., only when below a threshold).
  • The various logID == 0 early-return checks are now duplicated across several query functions; consider extracting the log-ID lookup plus zero-handling into a helper to keep the behavior consistent and reduce repetition.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `V160LogIDZeroCleanMigrate`, collecting all matching IDs into slices just for logging can be expensive on large datasets; consider logging only `RowsAffected` or limiting/plucking IDs conditionally (e.g., only when below a threshold).
- The various `logID == 0` early-return checks are now duplicated across several query functions; consider extracting the log-ID lookup plus zero-handling into a helper to keep the behavior consistent and reduce repetition.

## Individual Comments

### Comment 1
<location> `migrate/v2/v160/v160_logid0.go:12-51` </location>
<code_context>
+func V160LogIDZeroCleanMigrate(dboperator operator.DatabaseOperator, logf func(string)) error {
+	db := dboperator.GetLogDB(constant.WRITE)
+
+	var itemIDs []uint64
+	if err := db.Model(&model.LogOneItem{}).
+		Where("log_id = 0").
+		Pluck("id", &itemIDs).Error; err != nil {
+		return err
+	}
+	itemResult := db.Where("log_id = 0").Delete(&model.LogOneItem{})
+	if itemResult.Error != nil {
+		return itemResult.Error
+	}
+
+	var logIDs []uint64
+	if err := db.Model(&model.LogInfo{}).
+		Where("id = 0").
+		Pluck("id", &logIDs).Error; err != nil {
+		return err
+	}
+	logResult := db.Where("id = 0").Delete(&model.LogInfo{})
+	if logResult.Error != nil {
+		return logResult.Error
+	}
+
+	if len(itemIDs) > 0 {
+		logf(fmt.Sprintf("数据修复 - LogItems表,删除了 %d 条记录,ID列表: %v", itemResult.RowsAffected, itemIDs))
+	} else {
+		logf("数据修复 - LogItems表,没有需要删除的记录")
+	}
+
+	if len(logIDs) > 0 {
+		logf(fmt.Sprintf("数据修复 - Logs表,删除了 %d 条记录,ID列表: %v", logResult.RowsAffected, logIDs))
+	} else {
</code_context>

<issue_to_address>
**suggestion (performance):** Consider limiting or summarizing logged ID lists to avoid excessive memory usage and log volume.

Loading all IDs into slices and logging the full lists (`itemIDs`, `logIDs`) can be expensive on large tables (thousands+ rows), both in memory and log volume. Instead, consider logging only `RowsAffected`, or at most a capped subset/summary of IDs (e.g., first N or aggregated ranges/counts) to keep the migration stable on large datasets.

```suggestion
 // V160LogIDZeroCleanMigrate 清理 log_id = 0 的日志行与日志表残留
func V160LogIDZeroCleanMigrate(dboperator operator.DatabaseOperator, logf func(string)) error {
	db := dboperator.GetLogDB(constant.WRITE)

	itemResult := db.Where("log_id = 0").Delete(&model.LogOneItem{})
	if itemResult.Error != nil {
		return itemResult.Error
	}

	logResult := db.Where("id = 0").Delete(&model.LogInfo{})
	if logResult.Error != nil {
		return logResult.Error
	}

	if itemResult.RowsAffected > 0 {
		logf(fmt.Sprintf("数据修复 - LogItems表,删除了 %d 条记录", itemResult.RowsAffected))
	} else {
		logf("数据修复 - LogItems表,没有需要删除的记录")
	}

	if logResult.RowsAffected > 0 {
		logf(fmt.Sprintf("数据修复 - Logs表,删除了 %d 条记录", logResult.RowsAffected))
	} else {
		logf("数据修复 - Logs表,没有需要删除的记录")
	}

	return nil
}
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@kenichiLyon kenichiLyon changed the title Fix(log): 修复了当 log 名不存在时,使用指令会出现假 log 的情况 Fix: 修复了当 log 名不存在时,使用指令会出现假 log 的情况 Feb 8, 2026
@github-actions
Copy link

github-actions bot commented Feb 8, 2026

🚨 PR Title Needs Formatting

The title of this PR needs to be formatted correctly.
Please update the title to match the format type: description. Examples:

  • `fix: fix typo in README.md
  • chore: update dependencies
  • feat: add new feature
  • chore: fixing build pipeline
  • etc.
    More information can be found here.

@kenichiLyon kenichiLyon changed the title Fix: 修复了当 log 名不存在时,使用指令会出现假 log 的情况 fix: 修复了当 log 名不存在时,使用指令会出现假 log 的情况 Feb 8, 2026
@PaienNate
Copy link
Contributor

fix #1582

目前找到问题所在为:

  1. log new时先构造了 newLogItem,导致 logID==0 时日志项被写入 log_id=0;随后读取不存在日志时继续用 logID=0 查询,读回了这些“假log”
  2. 读取路径在获取 logID 时,如果不存在会返回 0 且不报错,随后用 log_id=0 去查行数据

本 pr 的解决方案对应为:

  1. 修改构造流程,为 newLogItem.LogID 赋值为 真实的 logid 再记录消息
  2. 个人推断问题 2 是设计使然,而其影响实际在用户升级时会体现,修复问题 1 后自然就不存在 log_id=0 的假 log,那么也就不存在问题 2 。为了保证兼容性与问题 2 可能的历史包袱,我因此加入了 migration 进行清理,预计放入 v160,这里可讨论

已自行测试,图中test-bot为此 commit 后编译的海豹,流明则是此 commit 前

image

前者是正确的,解释了log_id=0的查询来源。
后者是错误的。此处设计有失误之处,当查询不存在的日志条目时,应当抛出报错,而后返回该日志不存在的输出。
现在的代码中,不应判断err为未找到时,返回错误为nil;
另外,使用Scan时,err也不会产生这个查不到数据的报错。
可见上一位开发者开发时多半是喝了假酒才写出如此代码。
可能更加合理的改进路径是:修改此处查询逻辑,以及修改插入逻辑,插入时,先插入,拿到数据库通过Create自增得到的logid,再将log_id填充到日志中;查询时,如果发现查出的id为0则返回日志id为空未能正确查询等逻辑。

@kenichiLyon
Copy link
Contributor Author

fix #1582
目前找到问题所在为:

  1. log new时先构造了 newLogItem,导致 logID==0 时日志项被写入 log_id=0;随后读取不存在日志时继续用 logID=0 查询,读回了这些“假log”
  2. 读取路径在获取 logID 时,如果不存在会返回 0 且不报错,随后用 log_id=0 去查行数据

本 pr 的解决方案对应为:

  1. 修改构造流程,为 newLogItem.LogID 赋值为 真实的 logid 再记录消息
  2. 个人推断问题 2 是设计使然,而其影响实际在用户升级时会体现,修复问题 1 后自然就不存在 log_id=0 的假 log,那么也就不存在问题 2 。为了保证兼容性与问题 2 可能的历史包袱,我因此加入了 migration 进行清理,预计放入 v160,这里可讨论

已自行测试,图中test-bot为此 commit 后编译的海豹,流明则是此 commit 前
image

可能更加合理的改进路径是:修改此处查询逻辑,以及修改插入逻辑,插入时,先插入,拿到数据库通过Create自增得到的logid,再将log_id填充到日志中;查询时,如果发现查出的id为0则返回日志id为空未能正确查询等逻辑。

的确是我想得少了,已按照此路径进行修改

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

修复在查询不存在的 log 名称时,会因 log_id=0 的脏数据导致返回“假 log”的问题,并提供一次性迁移用于清理历史遗留数据。

Changes:

  • 调整 LogAppend 写入流程:确保创建新 log 后再把真实 logID 写回到 newLogItem.LogID,避免写入 log_id=0
  • getIDByGroupIDAndName 在未找到记录时返回明确的 ErrLogNotFound,并在多处读取路径中将“未找到”处理为返回空结果。
  • 新增 v160 迁移:清理 log_items.log_id=0logs.id=0 的历史残留数据,并在升级入口注册。

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
migrate/v2/v160/v160_logid0.go 新增清理 log_id=0 残留数据的 v160 升级迁移
migrate/v2/enter.go 注册 v160 迁移到升级管理器
dice/service/log.go 修复 log_id=0 写入源头,并用显式“未找到”错误避免读回假数据

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@kenichiLyon
Copy link
Contributor Author

kenichiLyon commented Feb 12, 2026

暂转为draft。
由于引入变量进行错误的统一处理,评估代码个人认为本修改对service层调用方的代码存在影响,需要一同修改service调用方相关的代码并进行对应的用户测试。

@kenichiLyon kenichiLyon marked this pull request as draft February 12, 2026 10:31
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]: 在查询不存在的日志时,会查询出logid=0的来源不明数据

2 participants