@@ -18,6 +18,8 @@ package ttrpc
1818
1919import (
2020 "context"
21+ "fmt"
22+ "sync"
2123 "testing"
2224)
2325
@@ -106,3 +108,100 @@ func TestMetadataContext(t *testing.T) {
106108 t .Errorf ("invalid metadata value: %q" , bar )
107109 }
108110}
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