1
0

reset_writer_test.go 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. package service
  2. import (
  3. "context"
  4. "testing"
  5. "time"
  6. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  7. "github.com/mhsanaei/3x-ui/v3/internal/web/runtime"
  8. )
  9. type blockingResetRuntime struct {
  10. fakeNodeRuntime
  11. reached chan struct{}
  12. release chan struct{}
  13. }
  14. func (b *blockingResetRuntime) ResetAllTraffics(context.Context) error {
  15. close(b.reached)
  16. <-b.release
  17. return nil
  18. }
  19. // A "Reset All Traffics" that propagates to nodes must not hold the single serial
  20. // traffic writer across the remote HTTP calls: each can block for seconds, and
  21. // stalling the writer drops every concurrent poll's traffic deltas.
  22. func TestResetAllTrafficsDoesNotBlockWriterOnNodeCall(t *testing.T) {
  23. db := initTrafficTestDB(t)
  24. resetTrafficWriterForTest(t)
  25. StartTrafficWriter()
  26. mgr := runtime.NewManager(runtime.LocalDeps{APIPort: func() int { return 0 }})
  27. runtime.SetManager(mgr)
  28. t.Cleanup(func() { runtime.SetManager(nil) })
  29. node := &model.Node{Name: "n1", Address: "127.0.0.1", Port: 2096, ApiToken: "tok", Enable: true, Status: "online"}
  30. if err := db.Create(node).Error; err != nil {
  31. t.Fatalf("create node: %v", err)
  32. }
  33. fake := &blockingResetRuntime{reached: make(chan struct{}), release: make(chan struct{})}
  34. mgr.SetRuntimeOverride(node.Id, fake)
  35. done := make(chan error, 1)
  36. go func() { done <- (&InboundService{}).ResetAllTraffics() }()
  37. select {
  38. case <-fake.reached:
  39. case <-time.After(3 * time.Second):
  40. close(fake.release)
  41. t.Fatal("node ResetAllTraffics was never reached")
  42. }
  43. writerFree := make(chan error, 1)
  44. go func() { writerFree <- submitTrafficWrite(func() error { return nil }) }()
  45. select {
  46. case err := <-writerFree:
  47. if err != nil {
  48. close(fake.release)
  49. t.Fatalf("concurrent writer submit failed: %v", err)
  50. }
  51. case <-time.After(2 * time.Second):
  52. close(fake.release)
  53. <-done
  54. t.Fatal("the serial traffic writer was blocked by a node reset HTTP call")
  55. }
  56. close(fake.release)
  57. if err := <-done; err != nil {
  58. t.Fatalf("ResetAllTraffics: %v", err)
  59. }
  60. }