完成数据库运行时回归并同步整改清单真实进度
This commit is contained in:
parent
0edde01e4b
commit
bf10eb50be
@ -17,7 +17,7 @@ require (
|
||||
golang.org/x/oauth2 v0.36.0
|
||||
golang.org/x/sys v0.47.0
|
||||
google.golang.org/api v0.288.0
|
||||
modernc.org/sqlite v1.40.1
|
||||
modernc.org/sqlite v1.59.0
|
||||
)
|
||||
|
||||
require (
|
||||
@ -78,11 +78,11 @@ require (
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.22 // indirect
|
||||
github.com/mattn/go-isatty v0.0.24 // indirect
|
||||
github.com/miekg/dns v1.1.72 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
@ -97,7 +97,6 @@ require (
|
||||
go.opentelemetry.io/otel/metric v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.44.0 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect
|
||||
golang.org/x/mod v0.38.0 // indirect
|
||||
golang.org/x/net v0.58.0 // indirect
|
||||
golang.org/x/sync v0.22.0 // indirect
|
||||
@ -108,8 +107,8 @@ require (
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/ini.v1 v1.67.3 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.66.10 // indirect
|
||||
modernc.org/libc v1.75.7 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
modernc.org/memory v1.12.1 // indirect
|
||||
software.sslmate.com/src/go-pkcs12 v0.7.3 // indirect
|
||||
)
|
||||
|
||||
@ -178,8 +178,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3 h1:LMLX+LgTNWpfvCBdFebv6EsYotImrt/Ppc5cXIriCSo=
|
||||
github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3/go.mod h1:jl5iWTm0/hd5PjEYEOuwAJ57L/CibdZfrqZ5XA5GrCk=
|
||||
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
@ -192,6 +192,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR
|
||||
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.205 h1:YnI2+56S5oxByz8BYChRrsmqTUmCp1Ww2KIzZtF4XOU=
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.205/go.mod h1:M+yna96Fx9o5GbIUnF3OvVvQGjgfVSyeJbV9Yb1z/wI=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
@ -227,8 +229,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
|
||||
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
||||
github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI=
|
||||
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
|
||||
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
|
||||
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@ -240,8 +242,8 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
@ -339,8 +341,6 @@ golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5D
|
||||
golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y=
|
||||
golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM=
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
@ -507,28 +507,30 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
modernc.org/cc/v4 v4.26.5 h1:xM3bX7Mve6G8K8b+T11ReenJOT+BmVqQj0FY5T4+5Y4=
|
||||
modernc.org/cc/v4 v4.26.5/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.28.1 h1:wPKYn5EC/mYTqBO373jKjvX2n+3+aK7+sICCv4Fjy1A=
|
||||
modernc.org/ccgo/v4 v4.28.1/go.mod h1:uD+4RnfrVgE6ec9NGguUNdhqzNIeeomeXf6CL0GTE5Q=
|
||||
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
|
||||
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/cc/v4 v4.29.2 h1:h6+9ciCnPKutf4I03CvheAvDLX7+IHlqR6Iy6J+cgd8=
|
||||
modernc.org/cc/v4 v4.29.2/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
|
||||
modernc.org/ccgo/v4 v4.35.0 h1:F+TUsmw09QxLzmi3aeYYGxjAXarmZaKgj3mKQHNaA8w=
|
||||
modernc.org/ccgo/v4 v4.35.0/go.mod h1:qrVGs9S3Sr2Ztcg9ve+kTAYMp5a3YvWjo+SoN06kJ5I=
|
||||
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
|
||||
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/gc/v3 v3.1.5 h1:21ldfPfRYE31Tb7B3mwAK8gy1AxP4+dKjrOQPfqakoc=
|
||||
modernc.org/gc/v3 v3.1.5/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.66.10 h1:yZkb3YeLx4oynyR+iUsXsybsX4Ubx7MQlSYEw4yj59A=
|
||||
modernc.org/libc v1.66.10/go.mod h1:8vGSEwvoUoltr4dlywvHqjtAqHBaw0j1jI7iFBTAr2I=
|
||||
modernc.org/libc v1.75.7 h1:o3DTP9/0p9pKmY2WCKQaySW6wIiZhNM7wc2lUoyhfew=
|
||||
modernc.org/libc v1.75.7/go.mod h1:bO5o2ztHxBb2rjz0PgdHN0sSMw57CgxGFLZ3Qd/QpVQ=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/memory v1.12.1 h1:nFMiWrpStgZczNl6XI9GnIk/rWhYIyHGUaR04pGbp9g=
|
||||
modernc.org/memory v1.12.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
|
||||
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.40.1 h1:VfuXcxcUWWKRBuP8+BR9L7VnmusMgBNNnBYGEe9w/iY=
|
||||
modernc.org/sqlite v1.40.1/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE=
|
||||
modernc.org/sqlite v1.59.0 h1:X1es1GpqBlS/5T+vbM4HLUdaa8OtQx468DF2vrx+38A=
|
||||
modernc.org/sqlite v1.59.0/go.mod h1:+paeT2A3iPRHkQDwG7oA6Tk0zQd5woMEI8q7orfry8k=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
||||
@ -235,6 +235,29 @@ func TestHistoryScopeMySQLMigrationRetry(t *testing.T) {
|
||||
|
||||
func assertMySQLHistoryIndexes(t *testing.T, db *sql.DB) {
|
||||
t.Helper()
|
||||
// 元数据字段正确不代表 InnoDB 内部索引一致;检查引擎报告但不自动修复表。
|
||||
for _, table := range []string{"tasks", "audit_logs"} {
|
||||
rows, err := db.QueryContext(t.Context(), "CHECK TABLE "+table)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ok := false
|
||||
for rows.Next() {
|
||||
var checkedTable, operation, kind, message string
|
||||
if err := rows.Scan(&checkedTable, &operation, &kind, &message); err != nil {
|
||||
_ = rows.Close()
|
||||
t.Fatal(err)
|
||||
}
|
||||
if kind == "status" && message == "OK" {
|
||||
ok = true
|
||||
} else {
|
||||
t.Errorf("迁移后的表完整性检查异常:%s %s %s %s", checkedTable, operation, kind, message)
|
||||
}
|
||||
}
|
||||
if err := errors.Join(rows.Err(), rows.Close()); err != nil || !ok {
|
||||
t.Fatalf("迁移后的表完整性检查未通过:%s,%v", table, err)
|
||||
}
|
||||
}
|
||||
for _, index := range []struct{ table, name, columns string }{
|
||||
{"tasks", "idx_tasks_project_history", "project_id,created_at"},
|
||||
{"audit_logs", "idx_audit_project_history", "project_id,created_at"},
|
||||
|
||||
131
backend/internal/database/sqlite_wal_integrity_test.go
Normal file
131
backend/internal/database/sqlite_wal_integrity_test.go
Normal file
@ -0,0 +1,131 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.pcux.cn/xuans/Scert/backend/internal/config"
|
||||
)
|
||||
|
||||
// 覆盖真实连接间的写入与检查点竞争,不将概率性压力测试当成上游缺陷的确定性复现。
|
||||
func TestSQLiteWALCheckpointIntegrity(t *testing.T) {
|
||||
cfg := config.Default(t.TempDir()).Database
|
||||
cfg.MaxOpenConns, cfg.MaxIdleConns = 3, 3
|
||||
db := openSQLiteTestPool(t, cfg)
|
||||
ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second)
|
||||
defer cancel()
|
||||
var version string
|
||||
if err := db.QueryRowContext(ctx, "SELECT sqlite_version()").Scan(&version); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("检查点并发完整性测试引擎:%s", version)
|
||||
if _, err := db.ExecContext(ctx, `CREATE TABLE wal_integrity (
|
||||
writer INTEGER NOT NULL, sequence INTEGER NOT NULL, part INTEGER NOT NULL,
|
||||
value INTEGER NOT NULL, PRIMARY KEY (writer, sequence, part))`); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
const batches = 256
|
||||
connections := make([]*sql.Conn, 3)
|
||||
for i := range connections {
|
||||
conn, err := db.Conn(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = conn.Close() })
|
||||
connections[i] = conn
|
||||
}
|
||||
start, writersDone := make(chan struct{}), make(chan struct{})
|
||||
results := make(chan error, 3)
|
||||
var writers sync.WaitGroup
|
||||
for writer := 0; writer < 2; writer++ {
|
||||
conn := connections[writer]
|
||||
writers.Add(1)
|
||||
go func() {
|
||||
defer writers.Done()
|
||||
defer conn.Close()
|
||||
<-start
|
||||
for sequence := 0; sequence < batches; sequence++ {
|
||||
if err := insertWALIntegrityPair(ctx, conn, writer, sequence); err != nil {
|
||||
results <- err
|
||||
return
|
||||
}
|
||||
runtime.Gosched()
|
||||
}
|
||||
results <- nil
|
||||
}()
|
||||
}
|
||||
checkpoint := connections[2]
|
||||
go func() {
|
||||
defer checkpoint.Close()
|
||||
<-start
|
||||
for {
|
||||
var busy, frames, copied int
|
||||
if err := checkpoint.QueryRowContext(ctx, "PRAGMA wal_checkpoint(PASSIVE)").Scan(&busy, &frames, &copied); err != nil {
|
||||
results <- err
|
||||
return
|
||||
}
|
||||
select {
|
||||
case <-writersDone:
|
||||
results <- nil
|
||||
return
|
||||
default:
|
||||
runtime.Gosched()
|
||||
}
|
||||
}
|
||||
}()
|
||||
close(start)
|
||||
writers.Wait()
|
||||
close(writersDone)
|
||||
for i := 0; i < 3; i++ {
|
||||
if err := <-results; err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
if t.Failed() {
|
||||
return
|
||||
}
|
||||
var busy, frames, copied int
|
||||
if err := db.QueryRowContext(ctx, "PRAGMA wal_checkpoint(TRUNCATE)").Scan(&busy, &frames, &copied); err != nil || busy != 0 {
|
||||
t.Fatalf("最终检查点未完成:busy=%d,错误=%v", busy, err)
|
||||
}
|
||||
if err := db.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// 重新打开文件验证已持久化的内容,不能只检查仍在连接缓存中的结果。
|
||||
reopened := openSQLiteTestPool(t, cfg)
|
||||
var integrity string
|
||||
if err := reopened.QueryRowContext(ctx, "PRAGMA integrity_check").Scan(&integrity); err != nil || integrity != "ok" {
|
||||
t.Fatalf("检查点竞争后数据库结构损坏:%s,%v", integrity, err)
|
||||
}
|
||||
var count, sum, invalid int
|
||||
if err := reopened.QueryRowContext(ctx, `SELECT COUNT(*), SUM(value),
|
||||
SUM(CASE WHEN writer NOT IN (0, 1) OR sequence < 0 OR sequence >= ?
|
||||
OR part NOT IN (0, 1) OR value <> writer * 10000 + sequence * 2 + part THEN 1 ELSE 0 END)
|
||||
FROM wal_integrity`, batches).Scan(&count, &sum, &invalid); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantSum := 2*batches*10000 + 2*batches*(2*batches-1)
|
||||
if count != 4*batches || sum != wantSum || invalid != 0 {
|
||||
t.Fatalf("检查点竞争后已提交数据不完整:条数=%d,总和=%d,错值=%d", count, sum, invalid)
|
||||
}
|
||||
}
|
||||
|
||||
func insertWALIntegrityPair(ctx context.Context, conn *sql.Conn, writer, sequence int) error {
|
||||
tx, err := conn.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
for part := 0; part < 2; part++ {
|
||||
if _, err := tx.ExecContext(ctx, "INSERT INTO wal_integrity VALUES (?, ?, ?, ?)",
|
||||
writer, sequence, part, writer*10000+sequence*2+part); err != nil {
|
||||
return fmt.Errorf("写入事务数据失败:%w", err)
|
||||
}
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
# Scert 开发任务清单
|
||||
|
||||
> 当前批次(2026-09-28):成功成员变更与审计同事务写入已实现,审计失败回滚;源码检查点不是正式发布。SQLite 候选为 v1.59.0,最后源码取消查询/WAL 专项、完整后端、双版本 MySQL 三轮组合及双平台构建通过;原 MySQL 索引异常根因未关闭。最新任务状态、最终竞态和证据见执行清单首节与支持矩阵第二十二节,不复用旧批次数字。下一开发重点为成员 Web/API、分页、失败审计、业务资源入口与排队任务重新判权。
|
||||
|
||||
> 当前批次(模式 14):项目成员多角色、实时判权、同语句授予/撤销及三库迁移已实现,SQLite/MySQL 双版本与独立 Windows 程序成员备份恢复通过。后续仍需成员管理 Web/API 与审计、全部资源入口和排队任务重判;本批基础模块不等于业务隔离完成。最新证据见执行清单首节与支持矩阵第二十一节。原始 44 项不缩减,P0-05/P0-06/P0-07 继续 `[~]`;下方为历史记录。
|
||||
|
||||
> 当前批次(模式 13):任务/审计范围快照、历史保留与回填、实际任务关联和操作者来源已实现;新增尝试拒绝缺失资源、项目变更和未知父任务。详细状态与测试以《Scert整改任务执行清单》首节和支持矩阵第二十节为准。其他资源、成员授权和生产验收继续实施,P0-05/P0-06/P0-07 保持 `[~]`,全部 44 项不缩减。下方各段保留历史证据,不表示最新状态。
|
||||
|
||||
@ -1,8 +1,22 @@
|
||||
# Scert 整改任务执行清单
|
||||
|
||||
## 2026 年 9 月 28 日:数据库最终候选(18:36,回归完成,等待复审)
|
||||
|
||||
- [x] 确认继续解决运行时和迁移阻塞,不删减原 44 项;源码修改前检查点 `0edde01` 已推送。
|
||||
- [x] 读取 5.7 初始化日志与 8.0 崩溃证据,确认两次内存分配失败;按可执行程序和隔离数据目录核验并停止两个残留测试进程,保留全部文件,不影响本机其他数据库服务。
|
||||
- [x] 核查官方 WAL-reset 修复说明和驱动版本源码,选择 `sqlite v1.59.0`/`libc v1.75.7`,实测引擎 3.53.4;旧 v1.40.1 测试降为中间证据。
|
||||
- [x] 最后源码取消查询与 WAL 完整性专项各 10 次通过,共 30 个叶子;服务并发关闭另重复 30 次通过。最终专项文件为 `sqlite-verified.jsonl`,初版 `sqlite-final.jsonl` 仅为中间证据。
|
||||
- [x] 在资源受限机器上以 `GOMAXPROCS=2`、`go test -p 1` 依次完成 MySQL 5.7/8.0 三轮原组合,各 531 个叶子通过、0 失败、0 跳过,退出码均为 0;不降低 TLS、不减少用例,包含 SQLite 用例。
|
||||
- [x] 最终完整后端 1635 个叶子通过、52 条条件跳过;Windows 独立程序历史备份/升级/成员恢复通过,静态检查、模块校验和双平台构建成功,最终顺序进程退出码为 0;Linux 仅交叉构建。
|
||||
- [x] 最终三轮扩大竞态 `race-final.jsonl`:831 个叶子通过、0 失败、15 条外部 MySQL 条件跳过,退出码为 0;不使用旧驱动的 828 个叶子通过数替代,也不把普通 MySQL 实库组合称为竞态验证。
|
||||
- [!] 旧索引元数据不一致根因未关闭;保留失败证据及生产门禁,不以重跑成功宣称修复。
|
||||
- [ ] 成员 Web/API、失败请求审计、全部业务入口与任务重判、PostgreSQL 当前实库及生产验收继续后续实施。
|
||||
|
||||
当前证据目录仍为 `artifacts/runtime-migration-20260928/`;最终批次为 `backend-final.jsonl`、`mysql57-final.jsonl`、`mysql80-final.jsonl`、`sqlite-verified.jsonl`、`service-close-final.jsonl` 与 `race-final.jsonl`,不覆盖旧失败记录。具体运行结果见支持矩阵第二十二节。原始 44 项状态不升级,下方章节按各自时间保留为历史。
|
||||
|
||||
## 2026 年 9 月 28 日:运行时与迁移回归(13:05 同步,双版本实库仍有阻塞)
|
||||
|
||||
本节是当前进度,以下 12:48 及更早章节为历史记录。事务审计源码已保存到检查点 `83a3ada`,不是正式验收发布;SQLite 驱动升级与本轮测试强化仍未提交。
|
||||
本节是 13:05 的历史快照,当前进度以首节为准。彼时事务审计源码已保存到检查点 `83a3ada`,不是正式验收发布;以下实现、失败和待办按当时状态保留,不覆盖最新结果。
|
||||
|
||||
- [x] 确认本次进度同步需求:核对实际源码状态、日志与进程退出码,同步总清单和执行清单,不用历史通过结果代替当前验证。
|
||||
- [x] 新增正式 SQLite 取消查询文件释放回归,旧驱动复现失败;候选 `modernc.org/sqlite v1.40.1` 下 direct/prepared 各 10 次通过,服务并发关闭连续 30 次通过,等待复审。
|
||||
|
||||
@ -1,6 +1,21 @@
|
||||
# Scert 整改、合并与优化清单
|
||||
|
||||
## 当前进度(2026 年 9 月 28 日 13:05,运行时与迁移回归批次)
|
||||
## 当前进度(2026 年 9 月 28 日 18:36,数据库候选回归完成,整体验收未通过)
|
||||
|
||||
本节更新下方 13:05 快照。检查点 `0edde01` 已提交推送;当前驱动改为 `modernc.org/sqlite v1.59.0`,配套 `libc v1.75.7`,实测引擎为 SQLite 3.53.4。旧 v1.40.1 的 3.50.4 引擎处于官方 WAL-reset 缺陷影响范围,先前通过只作为中间版本证据,不作为最终候选放行。
|
||||
|
||||
- [x] 核实两个资源失败:MySQL 5.7 初始化日志明确记录缓冲池内存分配失败;8.0 测试进程内存分配失败。已按程序路径和隔离数据目录验证身份,终止崩溃后残留的两个测试实例进程;未触碰本机其他服务,未删除文件。
|
||||
- [x] 最后源码的取消查询 direct/prepared 和 WAL 完整性专项各 10 次通过,共 30 个叶子;服务并发关闭另重复 30 次通过。最终专项证据为 `sqlite-verified.jsonl`,不是初版 `sqlite-final.jsonl`。
|
||||
- [x] MySQL 5.7/8.0 保留原用例、严格 TLS 和三轮重复,按版本依次执行;各 531 个叶子通过、0 失败、0 跳过,进程退出码均为 0。组合含 SQLite 用例,不称纯 MySQL 用例;表完整性和索引字段顺序检查通过不代表旧异常根因已修复。
|
||||
- [x] 最终候选完整后端 1635 个叶子通过、0 失败、52 条条件跳过;Windows 独立程序历史备份/升级/成员恢复、静态检查、模块校验和 Windows/Linux 候选构建通过,最终顺序进程退出码为 0。交叉构建不是 Linux 原生验收。
|
||||
- [x] 最终候选三轮扩大竞态 `race-final.jsonl`:831 个叶子通过、0 失败、15 条条件跳过,进程退出码为 0;跳过的是未配置外部 MySQL 的条件测试,实库证据单列,不复用 v1.40.1 的旧竞态数字。
|
||||
- [!] 旧 MySQL 5.7 Error 1280 根因仍未关闭,重复通过也不能自动关闭该问题。
|
||||
|
||||
原始 44 项:`[✓]` 0、`[x]` 3、`[~]` 27、`[!]` 3、`[ ]` 11。P0-04/P0-05/P0-06/P0-07/P1-09 仍为 `[~]`;本批子任务完成不升级整项状态,不按测试数量折算完成百分比,生产验收未通过。
|
||||
|
||||
接下来优先补齐成员管理 Web/API、分页、失败请求审计、业务资源授权和排队任务重判;PostgreSQL 当前实库、Linux 原生、完整发行包与实机服务验收仍未完成。最新证据目录为 `artifacts/runtime-migration-20260928/`,详细命令与边界见《构建版本与数据库支持矩阵》第二十二节;下方按时间保留历史结果,不代表当前待办。
|
||||
|
||||
## 历史进度快照(2026 年 9 月 28 日 13:05,运行时与迁移回归批次)
|
||||
|
||||
本节与《Scert整改任务执行清单.md》同步。成员基础已提交并推送为 `b3909d8`,事务审计源码已保存到检查点 `83a3ada`;检查点不代表验证完成或正式发布。当前 SQLite 驱动升级、取消查询回归及 MySQL 故障断言强化仍未提交。以下状态依据本次重新读取日志并取回全部四个测试进程退出码的结果,不沿用上一批完整回归结论。
|
||||
|
||||
@ -221,6 +236,8 @@ dist/
|
||||
|
||||
**状态:** `[~] 进行中`
|
||||
|
||||
**最新进度(2026-09-28):** SQLite 依赖已固定为 v1.59.0/libc v1.75.7,运行时引擎 3.53.4;最终候选完整后端、模块校验及双平台构建通过。最终竞态与完整发行门禁按首节及支持矩阵第二十二节记录,不沿用中间驱动结果放行。
|
||||
|
||||
统一记录并固定以下版本:
|
||||
|
||||
```text
|
||||
@ -247,6 +264,8 @@ npm.cmd run build
|
||||
|
||||
**状态:** `[~] 进行中`
|
||||
|
||||
**最新进度(2026-09-28):** 三库代码迁移已至模式 14;最后源码 SQLite 取消查询/WAL 专项、Windows 独立程序备份升级恢复通过,MySQL 5.7/8.0 成员与历史范围组合各三轮通过。旧 MySQL 索引异常根因仍未关闭,PostgreSQL 当前实库、Linux 原生及三库完整生产路径未验收;不能据局部通过升级总项状态。
|
||||
|
||||
项目当前涉及 SQLite、MySQL、PostgreSQL,需要明确正式支持范围。
|
||||
|
||||
建议定位:
|
||||
@ -279,6 +298,8 @@ backend/internal/database/migrations/sqlite/
|
||||
|
||||
**状态:** `[~] 进行中`
|
||||
|
||||
**最新进度(2026-09-28):** 已实现域名/服务器项目归属、同项目绑定、任务/审计范围快照与项目成员关系;成员成功变更和审计共用事务。并非下列全部核心实体均已交付;剩余资源关联与业务调用链仍需补齐,具体模型见《项目资源关联模型》第七至九节。
|
||||
|
||||
建议统一核心实体:
|
||||
|
||||
```text
|
||||
@ -314,6 +335,8 @@ backup_records 备份记录
|
||||
|
||||
**状态:** `[~] 进行中`
|
||||
|
||||
**最新进度(2026-09-28):** 仓储层已实现六类项目角色、实时判权、单语句授权变更与事务审计,取消/审计失败回滚及跨项目拒绝已有专项。成员管理 Web/API、分页、全部资源入口与排队任务执行/取消/重试重判尚未接入;仓储通过不等于对外权限隔离完成。
|
||||
|
||||
建议角色:
|
||||
|
||||
```text
|
||||
@ -732,6 +755,8 @@ frontend/src/views/Notifications/
|
||||
|
||||
**状态:** `[~] 进行中`
|
||||
|
||||
**最新进度(2026-09-28):** 已增加任务/项目范围和实际操作者快照,成员成功授予/撤销与审计同事务,审计失败回滚成员变化;失败请求审计、完整接口接入及本节列出的全部危险操作覆盖仍未完成。不能把成功审计专项通过标为整个审计中心完成。
|
||||
|
||||
页面建议:
|
||||
|
||||
```text
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
# 全面审查、测试与优化记录
|
||||
|
||||
> 最终候选结果同步(2026-09-28 18:36):完整后端 1635 个叶子通过、52 条条件跳过;MySQL 5.7/8.0 三轮组合各 531 个叶子通过且无失败跳过(包含 SQLite 用例);最后源码取消查询/WAL 专项共 30 个叶子通过,服务并发关闭另 30 次通过;最终三轮竞态 831 个叶子通过、15 条条件跳过。上述进程退出码均为 0。Windows 独立程序历史备份/升级/成员恢复、静态检查、模块校验及双平台构建通过,完整证据见支持矩阵第二十二节。旧索引异常根因仍未关闭,跳过与交叉构建不作为实机验收通过。
|
||||
|
||||
> 2026-09-28 运行时与迁移批次:SQLite 取消查询遗留文件占用在旧驱动复现;v1.40.1 中间候选专项和完整后端曾通过,但其 3.50.4 引擎仍处于官方 WAL-reset 缺陷影响范围,不能作为最终放行。当前改用 v1.59.0/3.53.4,并新增 WAL 并发事务持久化完整性回归。MySQL 两次资源分配失败已核实,旧索引异常仍未关闭。最终候选执行结果、命令和未覆盖范围见支持矩阵第二十二节;历史失败日志完整保留,不称生产验收通过。
|
||||
|
||||
> 2026-09-28 项目成员批次:模式 14 已增加六类项目角色、实时判权和原子变更。复现并修复 SQLite 负行号误拒绝、扩展唯一索引替换绕过、空邮箱误当唯一冲突三个边界,保留失败日志;最后修复后完整后端 1623 个叶子通过、51 项条件跳过,三次竞态共 633 个叶子通过,MySQL 双版本各 242 个叶子通过且无跳过(含 SQLite 用例),Windows 独立成员备份恢复通过。最新证据为支持矩阵第二十一节的 `*-verified.jsonl`;旧 `*-final.jsonl` 不代表最后修复后的代码。本批未运行浏览器或生产环境;项目 Web/API 与任务重新判权仍未接入,不宣称完成 P0-07。下方模式 13 为上一批历史记录。
|
||||
|
||||
> 当前批次:模式 13 任务/审计关联及身份来源复核。审计新增身份初版存在新登录漏记、携带旧 Cookie 时错记、更新自身账户后会话失效导致漏记的问题,已用正式回归复现并修复;不缓存权限,不恢复失效会话。首轮测试夹具问题与过严断言也分别保留原日志。本批最终证据及限制见支持矩阵第二十节,不能复用下方模式 12 的数字替代本批验证。
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
# 基础整改任务规格
|
||||
|
||||
> 当前补充(2026-09-28):P0-07/P1-09 的成功成员变更与审计同事务实现已在检查点保存,审计失败必须回滚;拒绝请求审计、成员 Web/API 和业务授权仍未交付。P0-04/P0-05 最终驱动候选的完整后端、SQLite 取消查询与 WAL 专项、双版本 MySQL 组合及 Windows 独立程序恢复已通过。精确结果、竞态与未关闭项见支持矩阵第二十二节,不沿用旧批次放行;各总项状态不升级。
|
||||
|
||||
> 本批进度(模式 14):P0-07 已实现项目成员与六类角色、按实时账户状态和关系判权、单语句授权变更;P0-06 增加成员引用约束。用户分组不隐式授予权限,普通用户不能读取完整私钥。成员 Web/API、审计和全部资源/任务授权链仍未接入;P0-05/P0-06/P0-07 保持 `[~]`。证据与边界见执行清单首节、模型第九节和支持矩阵第二十一节;下方为历史批次。
|
||||
|
||||
> 本批进度(模式 13):P0-06 已增加任务/审计的项目、系统和未知范围快照及任务关联;HTTP 身份、后台任务、历史删除资源与孤儿尝试按实际来源处理。范围保存与当前资源校验不是成员授权,P0-05/P0-06/P0-07 均保持 `[~]`。本批证据见执行清单首节及支持矩阵第二十节;下方模式 10~12 说明为历史批次,不覆盖本批结果。
|
||||
@ -169,7 +171,7 @@ P0-06 与 P0-07 均维持 `[~]`,六阶段中的隔离、生产和最终验收
|
||||
- 整改内容:资源增加项目、所有者、创建人及更新人;制定系统管理员、项目管理员、证书/DNS/部署管理员、审计与只读角色矩阵;列表、详情、写操作、导出与后台任务均检查项目归属,普通用户不能读完整私钥。
|
||||
- 验证命令:V07、V06、V03;补跨项目 ID 枚举、伪造归属、任务访问与私钥导出拒绝测试。
|
||||
- 实机验收步骤:建立两项目和最小权限成员;分别测试证书、服务器、目标、DNS、任务、审计的跨项目读写、删除与回滚;撤权后现有会话及排队任务必须重新判权。
|
||||
- 验收证据:域名/服务器归属持久化和 HTTP 拒绝伪造归属已覆盖;已有分组/角色测试及资源外键仍不能证明项目成员隔离。项目角色矩阵、所有资源的跨项目读写和真实撤权用例待补。
|
||||
- 验收证据:域名/服务器归属和 HTTP 拒绝伪造归属已覆盖;六类项目角色、按实际成员和账户状态判权、成功成员变更与审计同事务已有仓储实现及 SQLite/MySQL 隔离证据,当前候选结果见支持矩阵第二十二节。成员 Web/API、失败请求审计、全部资源跨项目读写和排队任务撤权重判仍待补,不能称项目隔离已完成。
|
||||
- 复审结果:未完成。
|
||||
|
||||
## P0-08 完善凭据和主密钥生命周期
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
# 构建版本与数据库支持矩阵
|
||||
|
||||
核查日期:2026 年 9 月 27 日;SQLite 补充核查:2026 年 9 月 28 日(本机时间),见第十五、十六节。本文区分代码支持、隔离测试和正式生产准入,不把驱动存在视为兼容性验收。
|
||||
首次核查日期:2026 年 9 月 27 日;最近结果同步:2026 年 9 月 28 日 18:36(本机时间),见第二十二节。本文区分代码支持、隔离测试和正式生产准入,不把驱动存在视为兼容性验收。
|
||||
|
||||
当前运行时候选及最后验证以第二十二节为准;第十五至二十一节保留各自批次的源码、依赖和环境证据,不据此推断新驱动已完成相同范围的验证。
|
||||
|
||||
## 一、工具链与依赖
|
||||
|
||||
@ -13,7 +15,7 @@
|
||||
| Vue | `frontend/package.json`、`frontend/package-lock.json` | `3.5.41` | 直接依赖固定精确版本,声明及锁文件必须一致 |
|
||||
| Vite | 同上 | `6.4.3` | 同上 |
|
||||
| Vue 构建插件 | 同上 | `5.2.4` | 同上 |
|
||||
| SQLite Go 驱动 | `backend/go.mod` | `modernc.org/sqlite v1.37.0` | 驱动版本不等于数据库引擎版本 |
|
||||
| SQLite Go 驱动 | `backend/go.mod` | `modernc.org/sqlite v1.59.0`;`libc v1.75.7` | 当前实测引擎 3.53.4;最终回归及限制见第二十二节 |
|
||||
| MySQL Go 驱动 | 同上 | `github.com/go-sql-driver/mysql v1.9.3` | 不代表 MySQL 所有服务端版本均通过验收 |
|
||||
| PostgreSQL Go 驱动 | 同上 | `github.com/jackc/pgx/v5 v5.9.2` | 同上 |
|
||||
| 编译目标 | `build/build-release.ps1` | `windows/amd64`、`linux/amd64` | 核心二进制 `CGO_ENABLED=0`、`-trimpath` |
|
||||
@ -55,10 +57,10 @@ npm.cmd run build
|
||||
|
||||
| 数据库 | 预期定位 | 代码现状 | 本轮验证 | 尚未满足的正式验收 |
|
||||
|---|---|---|---|---|
|
||||
| SQLite | 单机、开发、轻量部署 | 驱动、初始化、迁移 1~9、CLI 备份恢复、逐连接配置、截止时间等待预算及恢复前日志处理 | 保留迁移/并发证据;修复异常退出 WAL 覆盖恢复数据及历史任务数据升级失败,验证当前业务样本、历史模式升级、凭据及证书恢复,专项 71 个叶子用例通过,见第十六、十七节 | 取消边界、旧发行包实际数据、完整发行包、Linux 原生、真实服务重启及生产门禁 |
|
||||
| MySQL 5.7.44 | 历史兼容目标,仍保留实现 | 独立方言、迁移、连接恢复、InnoDB 快照、特殊列、自有触发器/例程/事件/视图、完整清单权限及受限官方恢复 | `777c716` 完整专项 176 个叶子用例通过,详见第十四节 | 更多编码与 SQL 模式、并发 DDL/授权变化、定义者迁移、独立发行命令、Linux 实库、Web 初始化及生产系统验证 |
|
||||
| MySQL 8.0.45 | 正式生产支持目标 | 同上,额外处理不可见列实际数据、启用角色与部分撤销 | 同候选独立实库 202 个叶子用例通过,未复用 5.7 的结果;详见第十四节 | 同上;不代表 8.0 全部补丁版本已经验证 |
|
||||
| PostgreSQL | 正式生产支持目标,实际服务端版本待本轮复核 | 独立方言、迁移 1~9、严格 TLS 和官方备份恢复工具接入 | 文件/嵌入/登记一致性、凭据/TLS 单元测试 | 当前版本真实集群、初始化、升级、失败回滚、备份恢复和业务回归 |
|
||||
| SQLite | 单机、开发、轻量部署 | 驱动 v1.59.0、实测引擎 3.53.4、迁移 1~14、CLI 备份恢复、项目成员与事务审计基础 | 当前取消查询和 WAL 专项 30 个叶子通过,Windows 独立程序历史备份/升级/成员恢复通过,见第二十二节;第十六、十七节保留历史证据 | 旧发行包实际数据、完整发行包、Linux 原生、真实服务重启、完整业务授权及生产门禁 |
|
||||
| MySQL 5.7.44 | 历史兼容目标,仍保留实现 | 独立方言、迁移 1~14、连接恢复、InnoDB 快照、官方备份恢复及项目成员事务审计 | 当前成员/历史范围三轮组合 531 个叶子通过、无失败跳过,含 SQLite 用例,见第二十二节;`777c716` 的 176 个完整专项叶子用例为第十四节历史批次 | 旧索引元数据异常根因、更多编码与 SQL 模式、并发 DDL/授权变化、定义者迁移、独立发行命令、Linux 实库、Web 初始化及生产验证 |
|
||||
| MySQL 8.0.45 | 正式生产支持目标 | 同上,额外处理不可见列实际数据、启用角色与部分撤销 | 当前独立三轮组合 531 个叶子通过、无失败跳过,含 SQLite 用例,见第二十二节;第十四节的 202 个完整专项为历史批次,不冒充本轮全范围验证 | 更多编码与 SQL 模式、并发 DDL/授权变化、定义者迁移、独立发行命令、Linux 实库、Web 初始化及生产验证;不代表全部补丁版本已验证 |
|
||||
| PostgreSQL | 正式生产支持目标,实际服务端版本待本轮复核 | 独立方言、迁移 1~14、严格 TLS 和官方备份恢复工具接入 | 文件/嵌入/登记一致性、凭据/TLS 单元测试;当前候选实库未执行 | 当前版本真实集群、初始化、升级、失败回滚、备份恢复和业务回归 |
|
||||
|
||||
正式支持目标不等于已经生产放行。旧文档中的 PostgreSQL 实机记录仅作为线索,不替代当前候选版本复测。本批 MySQL 测试自行初始化 loopback 独立实例,不连接本机已有服务或生产数据库;PostgreSQL 当前候选实库测试未执行。
|
||||
|
||||
@ -913,3 +915,106 @@ go test -json -count=1 -timeout=8m -run 'ProjectMembers|HistoryScope|ResourcePro
|
||||
- [ ] 成员与资源规模扩大后的查询/触发器性能准入。
|
||||
|
||||
本批子项 `[x]` 等待复审;P0-05、P0-06、P0-07 总项仍为 `[~]`。原始 44 项及最终生产验收范围保持不变。
|
||||
|
||||
## 二十二、成员事务审计、SQLite 驱动与运行时回归(2026 年 9 月 28 日)
|
||||
|
||||
### 22.1 本批实现与版本边界
|
||||
|
||||
- 成员成功授予/撤销和审计共用数据库事务;审计插入失败或影响行数不是 1 时回滚成员变更,保留实际操作者、项目、目标账户和角色。MySQL 非 InnoDB 成员表/审计表拒绝变更,不自动修改存储引擎。源码先保存于检查点 `83a3ada`,不将检查点视为发布。
|
||||
- 原 `modernc.org/sqlite v1.37.0` 取消查询在驱动丢弃结果集时可遗留 statement;正式 `TestSQLiteCanceledQueriesReleaseFiles` 的 direct/prepared 均复现旧驱动失败。断言包括 WAL 检查点、数据库关闭及立即重命名文件,没有睡眠重试关闭,也未跳过公共服务关闭测试。
|
||||
- v1.40.1 修复候选解决了上述结果集释放路径,并通过中间回归,但实测引擎为 3.50.4。SQLite 官方 WAL 文档列出该版本在 WAL-reset 缺陷影响范围内,因此不将此候选作为最终版本。
|
||||
- 最终验证候选固定为 `sqlite v1.59.0`/`libc v1.75.7`/`memory v1.12.1`,运行时查询引擎版本为 3.53.4。依赖来源为 Go 校验模块和对应版本源码;不将版本升级表述为解决所有未知安全问题。
|
||||
- 新增 `TestSQLiteWALCheckpointIntegrity`:三个真实连接,两个写入者各 256 个双行事务与检查点并发;最终 TRUNCATE 检查点后关闭并重新打开文件,验证 `integrity_check`、1024 条记录、主键范围、逐行值及总和。它是应用回归,不是官方 WAL-reset 缺陷的确定性复现。
|
||||
- `TestHistoryScopeMySQLMigrationRetry` 必须命中预期错误 1644 与注入消息;失败后和每次重试后校验三条索引字段顺序及 `CHECK TABLE`。不能把任意迁移失败当作故障注入成功,也不使用 REPAIR/自动重建来掩盖元数据异常。
|
||||
|
||||
本批未修改生产迁移 SQL、服务关闭或续期调度逻辑,模式仍为 14。上游核查入口(2026-09-28 读取):
|
||||
|
||||
```text
|
||||
https://sqlite.org/wal.html#the_wal_reset_bug
|
||||
https://gitlab.com/cznic/sqlite/-/blob/v1.59.0/CHANGELOG.md
|
||||
https://gitlab.com/cznic/sqlite/-/blob/v1.59.0/go.mod
|
||||
```
|
||||
|
||||
### 22.2 保留的失败和中间证据
|
||||
|
||||
证据目录:`artifacts/project-member-audit-20260928/` 与 `artifacts/runtime-migration-20260928/`,文件均不进入源码提交。
|
||||
|
||||
| 批次/文件 | 已核实结果 | 解释 |
|
||||
|---|---|---|
|
||||
| 旧驱动 `sqlite-cancel-before.jsonl` | direct/prepared 失败,退出码 1 | 取消查询遗留读事务与文件占用的回归证据 |
|
||||
| v1.40.1 `sqlite-cancel-after.jsonl`、`service-close-after.jsonl` | 20 个取消查询叶子及 30 次服务关闭通过 | 仅为中间驱动证据 |
|
||||
| v1.40.1 `backend.jsonl`、`race.jsonl` | 全后端 1634 个叶子通过、52 跳过;竞态 828 个叶子通过、15 跳过 | 不替代 v1.59.0 最终验证 |
|
||||
| `mysql57-after.jsonl` | 530 个叶子通过、1 失败,退出码 1 | 服务端日志确认初始化阶段缓冲池内存分配失败;包含 SQLite 用例 |
|
||||
| `mysql80-after.jsonl` | 完成 247 个叶子通过,数据库包进程异常终止,退出码 1 | Go 运行时无法分配内存,中断用例不能计通过 |
|
||||
| 旧 `mysql57-verified.jsonl` | 历史迁移重试发生 Error 1280 | 服务端同时报 InnoDB 内部 3 个索引与 MySQL 元数据 2 个索引不一致;与后来内存失败不是同一个已证实根因 |
|
||||
|
||||
8.0 异常退出遗留的两个 mysqld 进程,经绝对程序路径和隔离数据目录核验后终止;未停止本机其他 MySQL 服务,未删除测试文件或清理生产库。保留旧失败,不能用后续重跑覆盖失败事实。
|
||||
|
||||
### 22.3 最终候选验证记录
|
||||
|
||||
当前采用 `GOMAXPROCS=2` 与 `go test -p 1`,按版本依次运行 MySQL,避免多批重型测试同时消耗内存;并未禁止测试内部并发,也未降低 TLS 或减少用例。进程退出码必须取回,不能只凭日志末尾出现 PASS。
|
||||
|
||||
- [x] 最后源码的 `sqlite-verified.jsonl`:direct/prepared/WAL 各重复 10 次,共 30 个叶子通过、0 失败;初版 `sqlite-final.jsonl` 不作最后源码证据。
|
||||
- [x] `mysql57-final.jsonl` 与 `mysql80-final.jsonl`:各三轮累计 531 个叶子通过、0 失败、0 跳过,两个进程退出码均为 0;组合包含 SQLite 用例,不称 531 个纯 MySQL 测试。
|
||||
- [x] `backend-final.jsonl`:1635 个叶子通过、0 失败、52 条条件跳过;条件跳过不算通过。独立 Windows 产品程序的 `TestSQLiteBusinessUpgradeStandaloneCLI/historical_backup` 与 `upgraded_backup` 均通过。
|
||||
- [x] `service-close-final.jsonl`:并发关闭重复 30 次通过;最终 Windows 构建、`go vet -p 1 ./...`、`go mod verify` 和 Linux 交叉构建成功,全部顺序步骤退出码为 0,18:29 已取回最后进程结果。
|
||||
- [x] `race-final.jsonl`:最后候选三轮扩大竞态 831 个叶子通过、0 失败、15 条条件跳过,进程退出码为 0。五个外部 MySQL 条件测试各跳过三次,不将未带竞态的实库组合当作 MySQL 竞态通过;旧驱动的 828 个通过记录仅保留为历史。
|
||||
|
||||
候选二进制 SHA256(仅定位当前构建,不代表发行包或生产放行):
|
||||
|
||||
| 目标 | SHA256 |
|
||||
|---|---|
|
||||
| Windows amd64 | `885497724A940695F45F02027FB48C9D754A89D21F9071E7EECF52C08534308A` |
|
||||
| Linux amd64 | `64B9807A1231EEA3F6CA3D3484837AEF5ED20EF925F7CB02B6716EE0646DCEB3` |
|
||||
|
||||
在 `backend/` 执行,JSONL 为当前 PowerShell 重定向编码;测试目录使用现有忽略的 `artifacts/runtime-migration-20260928/`,不创建辅助验收脚本。
|
||||
|
||||
```powershell
|
||||
$env:GOMAXPROCS = '2'
|
||||
go test -p 1 -json -count=10 -timeout=5m -run '^TestSQLite(CanceledQueriesReleaseFiles|WALCheckpointIntegrity)$' ./internal/database
|
||||
|
||||
# 两个版本依次执行;程序和产物目录分别指向各自版本,不连接机器上的现有实例。
|
||||
$env:SCERT_MYSQL_TEST_BIN = 'E:\DB\5.7\bin'
|
||||
$env:SCERT_MYSQL_TEST_ROOT = 'D:\Desktop\Scert\artifacts\runtime-migration-20260928\mysql57-final-isolated'
|
||||
go test -p 1 -json -count=3 -timeout=10m -run 'ProjectMember|HistoryScope' ./internal/repository ./internal/database
|
||||
$env:SCERT_MYSQL_TEST_BIN = 'E:\DB\8.0\bin'
|
||||
$env:SCERT_MYSQL_TEST_ROOT = 'D:\Desktop\Scert\artifacts\runtime-migration-20260928\mysql80-final-isolated'
|
||||
go test -p 1 -json -count=3 -timeout=10m -run 'ProjectMember|HistoryScope' ./internal/repository ./internal/database
|
||||
```
|
||||
|
||||
完整后端与独立程序验证在另一命令进程顺序执行,每一步检查退出码,失败即停止。以下为已执行命令,不需要新增辅助脚本;完整后端未设置外部 MySQL 程序目录,相关条件跳过不计作实库通过。
|
||||
|
||||
```powershell
|
||||
$env:GOMAXPROCS = '2'
|
||||
$env:CGO_ENABLED = '0'
|
||||
$env:GOOS = 'windows'
|
||||
$env:GOARCH = 'amd64'
|
||||
go build -p 1 -o ../build/review-scert-runtime-final-windows-amd64.exe ./cmd/scert
|
||||
$env:SCERT_SQLITE_TEST_CLI = 'D:\Desktop\Scert\build\review-scert-runtime-final-windows-amd64.exe'
|
||||
go test -p 1 -json -count=1 -timeout=12m ./... > ../artifacts/runtime-migration-20260928/backend-final.jsonl
|
||||
go test -p 1 -json -count=10 -timeout=5m -run '^TestSQLite(CanceledQueriesReleaseFiles|WALCheckpointIntegrity)$' ./internal/database > ../artifacts/runtime-migration-20260928/sqlite-verified.jsonl
|
||||
go test -p 1 -json -count=30 -timeout=5m -run '^TestDesktopConcurrentTransitionAndIdempotentClose$' ./service > ../artifacts/runtime-migration-20260928/service-close-final.jsonl
|
||||
go vet -p 1 ./...
|
||||
go mod verify
|
||||
$env:GOOS = 'linux'
|
||||
go build -p 1 -trimpath -o ../build/review-scert-runtime-final-linux-amd64 ./cmd/scert
|
||||
```
|
||||
|
||||
最终三轮竞态在前一批退出后单独运行,显式恢复 Windows 目标并启用 CGO;本轮不设置独立 CLI 或外部 MySQL 程序目录,因此仍须单列条件跳过:
|
||||
|
||||
```powershell
|
||||
$env:GOMAXPROCS = '2'
|
||||
$env:CGO_ENABLED = '1'
|
||||
$env:GOOS = 'windows'
|
||||
$env:GOARCH = 'amd64'
|
||||
go test -race -p 1 -json -count=3 -timeout=12m -run 'SQLite|ProjectMember|HistoryScope|ConcurrentTransitionAndIdempotentClose' ./internal/database ./internal/repository ./service > ../artifacts/runtime-migration-20260928/race-final.jsonl
|
||||
```
|
||||
|
||||
### 22.4 未关闭项与验收边界
|
||||
|
||||
- [!] 旧 Error 1280 的服务端索引元数据不一致根因未确定。强化断言及多轮未复现只能提高观测覆盖,不能标记根因修复或生产放行。
|
||||
- [ ] 成员管理 Web/API、分页和失败请求审计;全部资源的实际项目授权以及排队任务执行/取消/重试重判。
|
||||
- [ ] PostgreSQL 当前版本实库、Linux 原生、完整三库备份恢复、干净发行包安装、真实 Windows Service/systemd 重启及 12 层生产门禁。
|
||||
- [ ] 大规模数据和长时间资源稳定性;有限次数回归不代表长期泄漏或并发风险全部消除。
|
||||
|
||||
P0-04/P0-05/P0-06/P0-07/P1-09 总项继续 `[~]`,原始 44 项和最终验收标准不缩减。
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
# Scert 生产部署与运维说明
|
||||
|
||||
> 验证收尾同步(2026-09-28):最终候选已完成 Windows 独立程序历史备份/升级/成员恢复和 Windows/Linux 构建,产物指纹与最终回归见支持矩阵第二十二节。Linux 为交叉构建,未进行原生运行;本批没有清理生产数据库、执行 SSH 部署或真实服务重启,仍禁止将候选构建通过解释为生产准入。
|
||||
|
||||
> 当前运行时候选(2026-09-28):SQLite 驱动固定为 v1.59.0,配套 libc v1.75.7,实测引擎 3.53.4;模式仍为 14。旧 v1.40.1 的专项通过不意味着其引擎满足当前 WAL 数据完整性要求,不使用中间版本替代最终候选。升级需先验证正式备份、历史模式升级及恢复;不直接部署本批工作区或降低 TLS。当前验证与未关闭的 MySQL 索引异常见支持矩阵第二十二节,本节不是生产放行许可。
|
||||
|
||||
> 成员成功变更与审计已使用同一事务;MySQL 成员表和审计表必须为 InnoDB,遇到历史非事务表拒绝变更而非自动改引擎。失败请求审计、完整 Web/API 和业务授权仍未接入,不能以仓储回归替代权限隔离验收。
|
||||
|
||||
> 最新候选模式为 14:新增项目成员多角色关系与实时判权基础,不为历史账户自动授予默认项目权限,也不将用户分组转为项目权限。必须使用支持模式 14 的程序打开升级后的库;下方模式 13 及更早提示仅为历史批次。不能删除迁移记录强行降级。
|
||||
|
||||
> 成员关系引用的项目和账户不能直接删除或替换,必须先显式撤销关系。MySQL 成员外键匹配历史用户主键字符集,版本登记失败可重试,残缺同名表必须拒绝;SQLite 触发器冲突时回滚,不覆盖旧触发器。成员管理接口、界面和业务授权尚未全部接入;此候选不是生产放行版本。本批隔离验证、备份恢复和剩余缺口见支持矩阵第二十一节,未执行生产清库或部署。
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
# 项目资源关联模型
|
||||
|
||||
> 当前补充(2026-09-28):模式仍为 14。项目成员成功授予/撤销已与审计在同一事务实现,审计失败或未实际写入时回滚成员变化;MySQL 要求成员表和审计表均为 InnoDB。不是失败请求审计,也不等于 Web/API 与任务授权闭环。最终驱动候选的完整后端、双版本 MySQL 组合及 Windows 独立程序恢复已通过;最终证据、竞态结果与未关闭项以执行清单首节及支持矩阵第二十二节为准,下方旧批次数字不替代当前验证。
|
||||
|
||||
> 当前模式为 14:已新增项目成员多角色关系、实时判权及原子授予/撤销,见第九节。成员不是用户分组,不向历史账户自动授予默认项目权限。该模块尚未接入完整 Web/API 与任务执行链,不能据此宣称资源隔离完成。第七、八节及下方模式 13 提示是上一批历史记录;本批证据见支持矩阵第二十一节。
|
||||
|
||||
> 当前实现更新:第 11、12 号迁移已提交;本批新增第 13 号迁移和任务/审计范围快照,见第七、八节。第五节是项目根实体历史批次,不代表最新资源状态。本批测试及未覆盖范围以执行清单首节、支持矩阵第二十节为准;项目成员授权仍未完成。
|
||||
@ -124,7 +126,7 @@ SQLite 与 PostgreSQL 在迁移事务内回填,失败回滚;MySQL DDL 可能
|
||||
- `GrantProjectRole`、`RevokeProjectRole` 仅允许有效超级管理员或目标项目的项目管理员;授权条件与成员写入在同一条 SQL 中执行,防止应用层先查后写的间隙。MySQL 撤销使用自连接,不依赖不兼容的同表删除子查询。
|
||||
- 未知角色、无效目标、禁用操作者及越权变更拒绝;重复授予不会产生重复关系。项目管理员可撤销自己的角色,之后不能自行恢复;有效超级管理员保留恢复能力。
|
||||
- 判权结果不是长期许可。业务入口仍须从实际资源解析项目,并在执行副作用前重新判权;任务提交、排队、取消、重试及撤权竞态的完整接入尚未完成。
|
||||
- 成员管理接口、界面、分页、变更审计尚未交付。当前没有新开放项目 HTTP 路由;仓储方法不能直接作为无需会话验证的外部入口。
|
||||
- 成员成功变更与审计已使用同一事务,具体边界见 9.4。成员管理接口、界面、分页、失败请求审计尚未交付。当前没有新开放项目 HTTP 路由;仓储方法不能直接作为无需会话验证的外部入口。
|
||||
|
||||
### 9.3 迁移与恢复边界
|
||||
|
||||
@ -133,3 +135,12 @@ SQLite 使用外键和额外触发器保护关联,不依赖外键/递归触
|
||||
MySQL 从历史 `users.id` 读取实际字符集和排序规则建立成员外键,不采用可能已变更的库默认值;DDL 后版本登记失败可重试,保留已有成员。重试检查用户字段、角色枚举、主键和双亲外键,残缺同名表拒绝登记版本,不自行删除或修补业务表。PostgreSQL 使用事务迁移与外键;本批未完成其原生实库验证。
|
||||
|
||||
Windows 独立产品程序已验证升级前的历史备份以及含两个项目角色的新备份恢复;恢复后重新走正式入口并核对成员授权及跨项目拒绝。此证据不是完整发行包、真实服务重启、三库全部恢复或生产验收。
|
||||
|
||||
### 9.4 成员变更与审计事务
|
||||
|
||||
- `changeProjectRole` 在同一事务内调用带实时授权谓词的成员写入和成功审计;不调用另开连接的审计接口,避免脱离事务或阻塞单连接 SQLite。
|
||||
- 审计动作区分 `project_member.grant` 与 `project_member.revoke`,资源为目标账户,记录项目范围、实际操作者和角色;自我撤权后使用刚验证的变更上下文,不重新推测已失去的权限。
|
||||
- 成员影响行数不是 1、审计写入失败、触发器忽略插入或提交失败均不得返回成功。审计插入未实际写入时回滚已发生的成员变更。
|
||||
- MySQL 预先检查 `project_members` 与 `audit_logs` 都是 InnoDB;不以 BeginTx 返回成功推断非事务表也能回滚,不擅自修改历史引擎。
|
||||
- 单连接 SQLite、审计故障、取消上下文、重复操作、自我撤权、历史审计保留及双版本 MySQL 隔离用例是当前验证范围;PostgreSQL 当前实库和生产端到端尚未完成。
|
||||
- 成功审计不能替代失败请求审计。拒绝请求、Web/API 身份来源与所有业务入口及排队任务重判仍是后续任务,不能由这次仓储提交宣称完成。
|
||||
|
||||
Loading…
Reference in New Issue
Block a user