@@ -18,6 +18,8 @@ package ttrpc
18
18
19
19
import (
20
20
"context"
21
+ "fmt"
22
+ "sync"
21
23
"testing"
22
24
)
23
25
@@ -106,3 +108,100 @@ func TestMetadataContext(t *testing.T) {
106
108
t .Errorf ("invalid metadata value: %q" , bar )
107
109
}
108
110
}
111
+
112
+ func TestMetadataClone (t * testing.T ) {
113
+ var metadata MD
114
+ m2 := metadata .Clone ()
115
+ if m2 != nil {
116
+ t .Error ("MD.Clone() on nil metadata should return nil" )
117
+ }
118
+
119
+ metadata = MD {"nil" : nil , "foo" : {"bar" }, "baz" : {"qux" , "quxx" }}
120
+ m2 = metadata .Clone ()
121
+
122
+ if len (metadata ) != len (m2 ) {
123
+ t .Errorf ("unexpected number of keys: %d, expected: %d" , len (m2 ), len (metadata ))
124
+ }
125
+
126
+ for k , v := range metadata {
127
+ v2 , ok := m2 [k ]
128
+ if ! ok {
129
+ t .Errorf ("key not found: %s" , k )
130
+ }
131
+ if v == nil && v2 == nil {
132
+ continue
133
+ }
134
+ if v == nil || v2 == nil {
135
+ t .Errorf ("unexpected nil value: %v, expected: %v" , v2 , v )
136
+ }
137
+ if len (v ) != len (v2 ) {
138
+ t .Errorf ("unexpected number of values: %d, expected: %d" , len (v2 ), len (v ))
139
+ }
140
+ for i := range v {
141
+ if v [i ] != v2 [i ] {
142
+ t .Errorf ("unexpected value: %s, expected: %s" , v2 [i ], v [i ])
143
+ }
144
+ }
145
+ }
146
+ }
147
+
148
+ func TestMetadataCloneConcurrent (t * testing.T ) {
149
+ metadata := make (MD )
150
+ metadata .Set ("foo" , "bar" )
151
+
152
+ var wg sync.WaitGroup
153
+ for i := 0 ; i < 20 ; i ++ {
154
+ wg .Add (1 )
155
+ go func () {
156
+ defer wg .Done ()
157
+ m2 := metadata .Clone ()
158
+ m2 .Set ("foo" , "baz" )
159
+ }()
160
+ }
161
+ wg .Wait ()
162
+ // Concurrent modification should clone the metadata first to avoid panic
163
+ // due to concurrent map writes.
164
+ if val , ok := metadata .Get ("foo" ); ! ok {
165
+ t .Error ("metadata not found" )
166
+ } else if val [0 ] != "bar" {
167
+ t .Errorf ("invalid metadata value: %q" , val [0 ])
168
+ }
169
+ }
170
+
171
+ func simpleClone (src MD ) MD {
172
+ md := MD {}
173
+ for k , v := range src {
174
+ md [k ] = append (md [k ], v ... )
175
+ }
176
+ return md
177
+ }
178
+
179
+ func BenchmarkMetadataClone (b * testing.B ) {
180
+ for _ , sz := range []int {5 , 10 , 20 , 50 } {
181
+ b .Run (fmt .Sprintf ("size=%d" , sz ), func (b * testing.B ) {
182
+ metadata := make (MD )
183
+ for i := 0 ; i < sz ; i ++ {
184
+ metadata .Set ("foo" + fmt .Sprint (i ), "bar" + fmt .Sprint (i ))
185
+ }
186
+
187
+ for i := 0 ; i < b .N ; i ++ {
188
+ _ = metadata .Clone ()
189
+ }
190
+ })
191
+ }
192
+ }
193
+
194
+ func BenchmarkSimpleMetadataClone (b * testing.B ) {
195
+ for _ , sz := range []int {5 , 10 , 20 , 50 } {
196
+ b .Run (fmt .Sprintf ("size=%d" , sz ), func (b * testing.B ) {
197
+ metadata := make (MD )
198
+ for i := 0 ; i < sz ; i ++ {
199
+ metadata .Set ("foo" + fmt .Sprint (i ), "bar" + fmt .Sprint (i ))
200
+ }
201
+
202
+ for i := 0 ; i < b .N ; i ++ {
203
+ _ = simpleClone (metadata )
204
+ }
205
+ })
206
+ }
207
+ }
0 commit comments