145 lines
4.2 KiB
Go
145 lines
4.2 KiB
Go
|
|
// 检查和修复 cursor 表的脚本
|
|||
|
|
package main
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"context"
|
|||
|
|
"database/sql"
|
|||
|
|
"fmt"
|
|||
|
|
"log"
|
|||
|
|
"strings"
|
|||
|
|
"time"
|
|||
|
|
|
|||
|
|
_ "github.com/lib/pq"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
const (
|
|||
|
|
pgHost = "localhost"
|
|||
|
|
pgPort = 5432
|
|||
|
|
pgUser = "postgres"
|
|||
|
|
pgPassword = "postgres"
|
|||
|
|
pgDatabase = "trustlog"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func main() {
|
|||
|
|
fmt.Println("🔍 Cursor Table Check Tool")
|
|||
|
|
fmt.Println(strings.Repeat("=", 60))
|
|||
|
|
|
|||
|
|
// 连接数据库
|
|||
|
|
dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
|
|||
|
|
pgHost, pgPort, pgUser, pgPassword, pgDatabase)
|
|||
|
|
|
|||
|
|
db, err := sql.Open("postgres", dsn)
|
|||
|
|
if err != nil {
|
|||
|
|
log.Fatalf("Failed to connect: %v", err)
|
|||
|
|
}
|
|||
|
|
defer db.Close()
|
|||
|
|
|
|||
|
|
if err := db.Ping(); err != nil {
|
|||
|
|
log.Fatalf("Failed to ping: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fmt.Println("✅ Connected to PostgreSQL")
|
|||
|
|
fmt.Println()
|
|||
|
|
|
|||
|
|
ctx := context.Background()
|
|||
|
|
|
|||
|
|
// 1. 检查 cursor 表数据
|
|||
|
|
fmt.Println("📊 Current Cursor Table:")
|
|||
|
|
rows, err := db.QueryContext(ctx, "SELECT cursor_key, cursor_value, last_updated_at FROM trustlog_cursor ORDER BY last_updated_at DESC")
|
|||
|
|
if err != nil {
|
|||
|
|
log.Printf("Failed to query cursor table: %v", err)
|
|||
|
|
} else {
|
|||
|
|
defer rows.Close()
|
|||
|
|
|
|||
|
|
count := 0
|
|||
|
|
for rows.Next() {
|
|||
|
|
var key, value string
|
|||
|
|
var updatedAt time.Time
|
|||
|
|
rows.Scan(&key, &value, &updatedAt)
|
|||
|
|
fmt.Printf(" Key: %s\n", key)
|
|||
|
|
fmt.Printf(" Value: %s\n", value)
|
|||
|
|
fmt.Printf(" Updated: %v\n", updatedAt)
|
|||
|
|
fmt.Println()
|
|||
|
|
count++
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if count == 0 {
|
|||
|
|
fmt.Println(" ❌ No cursor records found!")
|
|||
|
|
fmt.Println()
|
|||
|
|
fmt.Println(" 问题原因:")
|
|||
|
|
fmt.Println(" - Cursor Worker 可能没有启动")
|
|||
|
|
fmt.Println(" - 或者初始化失败")
|
|||
|
|
fmt.Println()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. 检查 operation 表状态
|
|||
|
|
fmt.Println("📊 Operation Table Status:")
|
|||
|
|
|
|||
|
|
var totalCount int
|
|||
|
|
db.QueryRowContext(ctx, "SELECT COUNT(*) FROM operation").Scan(&totalCount)
|
|||
|
|
fmt.Printf(" Total operations: %d\n", totalCount)
|
|||
|
|
|
|||
|
|
var trustloggedCount int
|
|||
|
|
db.QueryRowContext(ctx, "SELECT COUNT(*) FROM operation WHERE trustlog_status = 'TRUSTLOGGED'").Scan(&trustloggedCount)
|
|||
|
|
fmt.Printf(" Trustlogged: %d\n", trustloggedCount)
|
|||
|
|
|
|||
|
|
var notTrustloggedCount int
|
|||
|
|
db.QueryRowContext(ctx, "SELECT COUNT(*) FROM operation WHERE trustlog_status = 'NOT_TRUSTLOGGED'").Scan(¬TrustloggedCount)
|
|||
|
|
fmt.Printf(" Not trustlogged: %d\n", notTrustloggedCount)
|
|||
|
|
|
|||
|
|
// 查询最早的记录
|
|||
|
|
var earliestTime sql.NullTime
|
|||
|
|
db.QueryRowContext(ctx, "SELECT MIN(created_at) FROM operation WHERE trustlog_status = 'NOT_TRUSTLOGGED'").Scan(&earliestTime)
|
|||
|
|
if earliestTime.Valid {
|
|||
|
|
fmt.Printf(" Earliest NOT_TRUSTLOGGED record: %v\n", earliestTime.Time)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fmt.Println()
|
|||
|
|
|
|||
|
|
// 3. 检查 cursor 和记录的时间关系
|
|||
|
|
if notTrustloggedCount > 0 {
|
|||
|
|
fmt.Println("⚠️ Problem Detected:")
|
|||
|
|
fmt.Printf(" 有 %d 条记录未存证\n", notTrustloggedCount)
|
|||
|
|
|
|||
|
|
var cursorValue sql.NullString
|
|||
|
|
db.QueryRowContext(ctx, "SELECT cursor_value FROM trustlog_cursor WHERE cursor_key = 'operation_scan'").Scan(&cursorValue)
|
|||
|
|
|
|||
|
|
if !cursorValue.Valid {
|
|||
|
|
fmt.Println(" Cursor 表为空!")
|
|||
|
|
fmt.Println()
|
|||
|
|
fmt.Println(" 可能的原因:")
|
|||
|
|
fmt.Println(" 1. Cursor Worker 从未启动")
|
|||
|
|
fmt.Println(" 2. PersistenceClient 没有启用 Cursor Worker")
|
|||
|
|
fmt.Println()
|
|||
|
|
fmt.Println(" 解决方案:")
|
|||
|
|
fmt.Println(" 1. 确保 PersistenceClient 配置了 EnableCursorWorker: true")
|
|||
|
|
fmt.Println(" 2. 手动初始化 cursor:")
|
|||
|
|
fmt.Println(" go run scripts/init_cursor.go")
|
|||
|
|
} else {
|
|||
|
|
cursorTime, _ := time.Parse(time.RFC3339Nano, cursorValue.String)
|
|||
|
|
fmt.Printf(" Cursor 时间: %v\n", cursorTime)
|
|||
|
|
|
|||
|
|
if earliestTime.Valid && earliestTime.Time.Before(cursorTime) {
|
|||
|
|
fmt.Println()
|
|||
|
|
fmt.Println(" ❌ 问题:Cursor 时间晚于最早的未存证记录!")
|
|||
|
|
fmt.Println(" 这些记录不会被处理。")
|
|||
|
|
fmt.Println()
|
|||
|
|
fmt.Println(" 解决方案:")
|
|||
|
|
fmt.Println(" 1. 重置 cursor 到更早的时间:")
|
|||
|
|
fmt.Printf(" UPDATE trustlog_cursor SET cursor_value = '%s' WHERE cursor_key = 'operation_scan';\n",
|
|||
|
|
earliestTime.Time.Add(-1*time.Second).Format(time.RFC3339Nano))
|
|||
|
|
fmt.Println()
|
|||
|
|
fmt.Println(" 2. 或者使用脚本重置:")
|
|||
|
|
fmt.Println(" go run scripts/reset_cursor.go")
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
fmt.Println("✅ All operations are trustlogged!")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fmt.Println()
|
|||
|
|
fmt.Println(strings.Repeat("=", 60))
|
|||
|
|
}
|
|||
|
|
|