Browse Source

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 month ago
parent
commit
73c6a3aa37

+ 6
- 2
mtglib/internal/doppel/scout.go View File

61
 		client.CloseIdleConnections()
61
 		client.CloseIdleConnections()
62
 	}
62
 	}
63
 
63
 
64
+	if err != nil {
65
+		return ScoutResult{}, err
66
+	}
67
+
64
 	data, writeIndex := results.Snapshot()
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
 	var result ScoutResult
74
 	var result ScoutResult

+ 48
- 4
mtglib/internal/doppel/scout_conn_collected_test.go View File

1
 package doppel
1
 package doppel
2
 
2
 
3
 import (
3
 import (
4
+	"sync"
4
 	"testing"
5
 	"testing"
5
 	"time"
6
 	"time"
6
 
7
 
16
 	collected := NewScoutConnCollected()
17
 	collected := NewScoutConnCollected()
17
 	collected.Add(tls.TypeApplicationData, 100)
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
 func (suite *ScoutConnCollectedTestSuite) TestAddTimestampsAreMonotonic() {
26
 func (suite *ScoutConnCollectedTestSuite) TestAddTimestampsAreMonotonic() {
31
 	time.Sleep(time.Microsecond)
34
 	time.Sleep(time.Microsecond)
32
 	collected.Add(tls.TypeApplicationData, 100)
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
 func TestScoutConnCollected(t *testing.T) {
83
 func TestScoutConnCollected(t *testing.T) {
40
 	t.Parallel()
84
 	t.Parallel()
41
 	suite.Run(t, &ScoutConnCollectedTestSuite{})
85
 	suite.Run(t, &ScoutConnCollectedTestSuite{})

Loading…
Cancel
Save