| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 |
- package main
- // =============================================================================
- // file_lock_verify — 跨进程文件锁验证
- //
- // 父进程:
- // 1. 先建一个数据库;
- // 2. 起子进程打开该库并持有锁 3 秒 (模拟另一个进程正在使用);
- // 3. 子进程持锁期间, 父进程尝试 Open 同一文件 —— 必须被拒绝, 且错误可用
- // errors.Is(err, core.ErrDatabaseLocked) 判定;
- // 4. 等子进程退出 (Close 释放锁);
- // 5. 父进程再次 Open —— 必须成功。
- //
- // 这是文件锁的端到端验证: 锁确实由 OS 内核跨进程互斥, 且释放后立即可用。
- // =============================================================================
- import (
- "errors"
- "fmt"
- "os"
- "os/exec"
- "path/filepath"
- "time"
- "git.ycl.pub/yl32940/TsDB2/DB/core"
- )
- const (
- envChild = "FILE_LOCK_CHILD"
- envDB = "FILE_LOCK_DB"
- holdSecs = 3
- )
- func main() {
- if os.Getenv(envChild) == "1" {
- runChild()
- return
- }
- runParent()
- }
- // runChild 打开数据库并持有锁 holdSecs 秒, 然后关闭释放。
- func runChild() {
- path := os.Getenv(envDB)
- db, err := core.Open(path)
- if err != nil {
- fmt.Fprintln(os.Stderr, "child open:", err)
- os.Exit(2)
- }
- time.Sleep(holdSecs * time.Second) // 持有锁, 模拟正在使用
- _ = db.Close()
- }
- func runParent() {
- dir, err := os.MkdirTemp("", "filelock-")
- if err != nil {
- panic(err)
- }
- defer os.RemoveAll(dir)
- path := filepath.Join(dir, "test.db")
- // 先建库
- initDB, err := core.Open(path)
- if err != nil {
- panic(err)
- }
- _ = initDB.Close()
- exe, err := os.Executable()
- if err != nil {
- panic(err)
- }
- cmd := exec.Command(exe)
- cmd.Env = append(os.Environ(), envChild+"=1", envDB+"="+path)
- if err := cmd.Start(); err != nil {
- panic(err)
- }
- pass, fail := 0, 0
- check := func(name string, ok bool) {
- if ok {
- fmt.Printf(" [OK] %s\n", name)
- pass++
- } else {
- fmt.Printf(" [FAIL] %s\n", name)
- fail++
- }
- }
- // 等子进程打开并持有锁
- time.Sleep(1000 * time.Millisecond)
- // 1. 子进程持锁时, 父进程 Open 必须被拒绝
- _, err = core.Open(path)
- check(fmt.Sprintf("另一进程持锁时 Open 被拒绝 (err=%v)", err),
- errors.Is(err, core.ErrDatabaseLocked))
- // 2. 等子进程退出释放锁
- _ = cmd.Wait()
- // 3. 锁释放后父进程 Open 必须成功
- db2, err2 := core.Open(path)
- check(fmt.Sprintf("锁释放后 Open 成功 (err=%v)", err2), err2 == nil && db2 != nil)
- if db2 != nil {
- _ = db2.Close()
- }
- fmt.Printf("\n总计: 通过 %d, 失败 %d\n", pass, fail)
- if fail > 0 {
- os.Exit(1)
- }
- }
|