mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-09-28 07:36:38 +08:00
2df061a19f
- Fix deadlock when updating tunnel entries with offline nodes - Add test file for tunnel entry SQLite operations - Update contract tests for entry port conflict and limiter sync - Add plan documents for SQLite deadlock fix and contract semantics
105 lines
3.2 KiB
Go
105 lines
3.2 KiB
Go
package handler
|
|
|
|
import (
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"go-backend/internal/store/repo"
|
|
)
|
|
|
|
func TestValidateTunnelEntryPortConflictsForNewEntriesDoesNotBlockOnSQLiteTx(t *testing.T) {
|
|
r, err := repo.Open(filepath.Join(t.TempDir(), "panel.db"))
|
|
if err != nil {
|
|
t.Fatalf("open sqlite: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
_ = r.Close()
|
|
})
|
|
h := &Handler{repo: r}
|
|
now := time.Now().UnixMilli()
|
|
|
|
if err := r.DB().Exec(`
|
|
INSERT INTO node(name, secret, server_ip, port, created_time, status, tcp_listen_addr, udp_listen_addr, is_remote)
|
|
VALUES
|
|
('entry-old', 'secret-old', '10.0.0.1', '12000-12010', ?, 1, '[::]', '[::]', 0),
|
|
('entry-new', 'secret-new', '10.0.0.2', '12000-12010', ?, 1, '[::]', '[::]', 0)
|
|
`, now, now).Error; err != nil {
|
|
t.Fatalf("insert nodes: %v", err)
|
|
}
|
|
var oldEntryID, newEntryID int64
|
|
if err := r.DB().Raw(`SELECT id FROM node WHERE name = 'entry-old'`).Scan(&oldEntryID).Error; err != nil {
|
|
t.Fatalf("load old entry id: %v", err)
|
|
}
|
|
if err := r.DB().Raw(`SELECT id FROM node WHERE name = 'entry-new'`).Scan(&newEntryID).Error; err != nil {
|
|
t.Fatalf("load new entry id: %v", err)
|
|
}
|
|
|
|
if err := r.DB().Exec(`
|
|
INSERT INTO tunnel(name, traffic_ratio, type, protocol, flow, created_time, updated_time, status, inx, ip_preference)
|
|
VALUES('sqlite-tunnel', 1, 1, 'tls', 1, ?, ?, 1, 1, '')
|
|
`, now, now).Error; err != nil {
|
|
t.Fatalf("insert tunnel: %v", err)
|
|
}
|
|
var tunnelID int64
|
|
if err := r.DB().Raw(`SELECT id FROM tunnel WHERE name = 'sqlite-tunnel'`).Scan(&tunnelID).Error; err != nil {
|
|
t.Fatalf("load tunnel id: %v", err)
|
|
}
|
|
|
|
if err := r.DB().Exec(`
|
|
INSERT INTO chain_tunnel(tunnel_id, chain_type, node_id, inx, protocol)
|
|
VALUES(?, '1', ?, 1, 'tls')
|
|
`, tunnelID, oldEntryID).Error; err != nil {
|
|
t.Fatalf("insert chain_tunnel: %v", err)
|
|
}
|
|
|
|
if err := r.DB().Exec(`
|
|
INSERT INTO forward(user_id, user_name, name, tunnel_id, remote_addr, strategy, created_time, updated_time, status, inx)
|
|
VALUES(1, 'tester', 'forward-a', ?, '127.0.0.1:8080', 'fifo', ?, ?, 1, 1)
|
|
`, tunnelID, now, now).Error; err != nil {
|
|
t.Fatalf("insert forward: %v", err)
|
|
}
|
|
var forwardID int64
|
|
if err := r.DB().Raw(`SELECT id FROM forward WHERE name = 'forward-a'`).Scan(&forwardID).Error; err != nil {
|
|
t.Fatalf("load forward id: %v", err)
|
|
}
|
|
|
|
if err := r.DB().Exec(`
|
|
INSERT INTO forward_port(forward_id, node_id, port)
|
|
VALUES(?, ?, 12001)
|
|
`, forwardID, oldEntryID).Error; err != nil {
|
|
t.Fatalf("insert forward_port: %v", err)
|
|
}
|
|
|
|
tx := r.BeginTx()
|
|
if tx == nil {
|
|
t.Fatal("begin tx: nil transaction")
|
|
}
|
|
if tx.Error != nil {
|
|
t.Fatalf("begin tx: %v", tx.Error)
|
|
}
|
|
|
|
errCh := make(chan error, 1)
|
|
doneCh := make(chan struct{})
|
|
go func() {
|
|
defer close(doneCh)
|
|
errCh <- h.validateTunnelEntryPortConflictsForNewEntries(tx, tunnelID, []int64{oldEntryID}, []int64{oldEntryID, newEntryID})
|
|
}()
|
|
|
|
select {
|
|
case err := <-errCh:
|
|
if err != nil {
|
|
_ = tx.Rollback().Error
|
|
t.Fatalf("unexpected validation error: %v", err)
|
|
}
|
|
case <-time.After(500 * time.Millisecond):
|
|
_ = tx.Rollback().Error
|
|
<-doneCh
|
|
t.Fatal("validation blocked while transaction was open on sqlite")
|
|
}
|
|
|
|
if err := tx.Rollback().Error; err != nil {
|
|
t.Fatalf("rollback tx: %v", err)
|
|
}
|
|
}
|