Просмотр исходного кода

fix: tighten ScoutConnCollected encapsulation and add concurrency test

- Move error check before Snapshot() to avoid unnecessary allocation
- Update existing tests to use Snapshot() instead of direct field access
- Add TestConcurrentAddSnapshot to explicitly exercise the mutex
tags/v2.2.7^2^2
Alexey Dolotov 1 месяц назад
Родитель
Сommit
73c6a3aa37
2 измененных файлов: 54 добавлений и 6 удалений
  1. 6
    2
      mtglib/internal/doppel/scout.go
  2. 48
    4
      mtglib/internal/doppel/scout_conn_collected_test.go

+ 6
- 2
mtglib/internal/doppel/scout.go Просмотреть файл

@@ -61,10 +61,14 @@ func (s Scout) learn(ctx context.Context, url string) (ScoutResult, error) {
61 61
 		client.CloseIdleConnections()
62 62
 	}
63 63
 
64
+	if err != nil {
65
+		return ScoutResult{}, err
66
+	}
67
+
64 68
 	data, writeIndex := results.Snapshot()
65 69
 
66
-	if err != nil || len(data) == 0 {
67
-		return ScoutResult{}, err
70
+	if len(data) == 0 {
71
+		return ScoutResult{}, nil
68 72
 	}
69 73
 
70 74
 	var result ScoutResult

+ 48
- 4
mtglib/internal/doppel/scout_conn_collected_test.go Просмотреть файл

@@ -1,6 +1,7 @@
1 1
 package doppel
2 2
 
3 3
 import (
4
+	"sync"
4 5
 	"testing"
5 6
 	"time"
6 7
 
@@ -16,8 +17,10 @@ func (suite *ScoutConnCollectedTestSuite) TestAddSingle() {
16 17
 	collected := NewScoutConnCollected()
17 18
 	collected.Add(tls.TypeApplicationData, 100)
18 19
 
19
-	suite.Len(collected.data, 1)
20
-	suite.Equal(byte(tls.TypeApplicationData), collected.data[0].recordType)
20
+	data, _ := collected.Snapshot()
21
+
22
+	suite.Len(data, 1)
23
+	suite.Equal(byte(tls.TypeApplicationData), data[0].recordType)
21 24
 }
22 25
 
23 26
 func (suite *ScoutConnCollectedTestSuite) TestAddTimestampsAreMonotonic() {
@@ -31,11 +34,52 @@ func (suite *ScoutConnCollectedTestSuite) TestAddTimestampsAreMonotonic() {
31 34
 	time.Sleep(time.Microsecond)
32 35
 	collected.Add(tls.TypeApplicationData, 100)
33 36
 
34
-	for i := 1; i < len(collected.data); i++ {
35
-		suite.True(collected.data[i].timestamp.After(collected.data[i-1].timestamp))
37
+	data, _ := collected.Snapshot()
38
+
39
+	for i := 1; i < len(data); i++ {
40
+		suite.True(data[i].timestamp.After(data[i-1].timestamp))
36 41
 	}
37 42
 }
38 43
 
44
+func (suite *ScoutConnCollectedTestSuite) TestConcurrentAddSnapshot() {
45
+	collected := NewScoutConnCollected()
46
+
47
+	var wg sync.WaitGroup
48
+
49
+	wg.Add(3)
50
+
51
+	go func() {
52
+		defer wg.Done()
53
+
54
+		for i := 0; i < 1000; i++ {
55
+			collected.Add(tls.TypeApplicationData, i)
56
+		}
57
+	}()
58
+
59
+	go func() {
60
+		defer wg.Done()
61
+
62
+		for i := 0; i < 100; i++ {
63
+			collected.MarkWrite()
64
+		}
65
+	}()
66
+
67
+	go func() {
68
+		defer wg.Done()
69
+
70
+		for i := 0; i < 1000; i++ {
71
+			data, _ := collected.Snapshot()
72
+			_ = len(data)
73
+		}
74
+	}()
75
+
76
+	wg.Wait()
77
+
78
+	data, writeIndex := collected.Snapshot()
79
+	suite.Len(data, 1000)
80
+	suite.GreaterOrEqual(writeIndex, 0)
81
+}
82
+
39 83
 func TestScoutConnCollected(t *testing.T) {
40 84
 	t.Parallel()
41 85
 	suite.Run(t, &ScoutConnCollectedTestSuite{})

Загрузка…
Отмена
Сохранить