github.com/nats-io/nats-server/v2@v2.11.0-preview.2/server/jetstream_meta_benchmark_test.go (about) 1 // Copyright 2024 The NATS Authors 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 //go:build !skip_js_tests && !skip_js_cluster_tests && !skip_js_cluster_tests_2 15 // +build !skip_js_tests,!skip_js_cluster_tests,!skip_js_cluster_tests_2 16 17 package server 18 19 import ( 20 "fmt" 21 "sync" 22 "sync/atomic" 23 "testing" 24 25 "github.com/nats-io/nats.go" 26 ) 27 28 func BenchmarkJetStreamCreate(b *testing.B) { 29 30 const ( 31 verbose = false 32 resourcePrefix = "S" 33 concurrency = 12 34 ) 35 36 // Types of resource that this benchmark creates 37 type ResourceType string 38 const ( 39 Stream ResourceType = "Stream" 40 KVBucket ResourceType = "KVBucket" 41 ObjectStore ResourceType = "ObjStore" 42 ) 43 44 resourceTypeCases := []ResourceType{ 45 Stream, 46 KVBucket, 47 ObjectStore, 48 } 49 50 benchmarksCases := []struct { 51 clusterSize int 52 replicas int 53 storage nats.StorageType 54 }{ 55 {1, 1, nats.MemoryStorage}, 56 {3, 3, nats.MemoryStorage}, 57 {3, 3, nats.FileStorage}, 58 } 59 60 for _, bc := range benchmarksCases { 61 bName := fmt.Sprintf( 62 "N=%d,R=%d,storage=%s,C=%d", 63 bc.clusterSize, 64 bc.replicas, 65 bc.storage.String(), 66 concurrency, 67 ) 68 69 b.Run( 70 bName, 71 func(b *testing.B) { 72 for _, rt := range resourceTypeCases { 73 //for _, bc := range benchmarksCases { 74 rName := fmt.Sprintf("resource=%s", rt) 75 b.Run( 76 rName, 77 func(b *testing.B) { 78 79 if verbose { 80 b.Logf( 81 "Creating %d %s resources in cluster with %d nodes, R=%d, %s storage", 82 b.N, 83 string(rt), 84 bc.clusterSize, 85 bc.replicas, 86 bc.storage, 87 ) 88 } 89 90 // Setup server or cluster 91 _, leaderServer, shutdown, nc, _ := startJSClusterAndConnect(b, bc.clusterSize) 92 defer shutdown() 93 defer nc.Close() 94 95 // All clients connect to cluster (meta) leader for lower variability 96 connectURL := leaderServer.ClientURL() 97 98 // Wait for all clients and main routine to be ready 99 wgReady := sync.WaitGroup{} 100 wgReady.Add(concurrency + 1) 101 // Wait for all routines to complete 102 wgComplete := sync.WaitGroup{} 103 wgComplete.Add(concurrency) 104 105 // Number of operations (divided amongst clients) 106 opsLeft := atomic.Int64{} 107 opsLeft.Store(int64(b.N)) 108 totalErrors := atomic.Int64{} 109 110 // Pre-create connections and JS contexts 111 for i := 1; i <= concurrency; i++ { 112 nc, js := jsClientConnectURL(b, connectURL) 113 defer nc.Close() 114 go func(clientId int, nc *nats.Conn, js nats.JetStreamContext) { 115 defer wgComplete.Done() 116 117 // Config struct (reused and modified in place for each call) 118 streamConfig := nats.StreamConfig{ 119 Name: "?", 120 Storage: bc.storage, 121 Replicas: bc.replicas, 122 } 123 kvConfig := nats.KeyValueConfig{ 124 Bucket: "?", 125 Storage: bc.storage, 126 Replicas: bc.replicas, 127 } 128 objConfig := nats.ObjectStoreConfig{ 129 Bucket: "?", 130 Storage: bc.storage, 131 Replicas: bc.replicas, 132 } 133 134 // Block until everyone is ready 135 wgReady.Done() 136 wgReady.Wait() 137 138 errCount := int64(0) 139 defer func() { 140 // Roll up error count on completion 141 totalErrors.Add(errCount) 142 }() 143 144 // Track per-client opCount (just for logging/debugging) 145 opCount := 0 146 for opsLeft.Add(-1) >= 0 { 147 var err error 148 // Create unique resource name 149 resourceName := fmt.Sprintf("%s_%d_%d", resourcePrefix, clientId, opCount) 150 switch rt { 151 case Stream: 152 streamConfig.Name = resourceName 153 _, err = js.AddStream(&streamConfig) 154 case KVBucket: 155 kvConfig.Bucket = resourceName 156 _, err = js.CreateKeyValue(&kvConfig) 157 case ObjectStore: 158 objConfig.Bucket = resourceName 159 _, err = js.CreateObjectStore(&objConfig) 160 } 161 opCount += 1 162 if err != nil { 163 b.Logf("Error creating %s (%s): %s", rt, resourceName, err) 164 errCount += 1 165 } 166 } 167 168 if verbose { 169 b.Logf("Client %d completed %d operations", clientId, opCount) 170 } 171 172 }(i, nc, js) 173 } 174 175 // Wait for all clients to be ready 176 wgReady.Done() 177 wgReady.Wait() 178 179 // Start benchmark clock 180 b.ResetTimer() 181 182 wgComplete.Wait() 183 b.StopTimer() 184 185 b.ReportMetric(float64(100*(totalErrors.Load()))/float64(b.N), "%error") 186 }, 187 ) 188 } 189 }, 190 ) 191 } 192 } 193 194 func BenchmarkJetStreamCreateConsumers(b *testing.B) { 195 196 const ( 197 verbose = false 198 streamName = "S" 199 consumerPrefix = "C" 200 concurrency = 12 201 ) 202 203 benchmarksCases := []struct { 204 clusterSize int 205 consumerReplicas int 206 consumerStorage nats.StorageType 207 }{ 208 {1, 1, nats.MemoryStorage}, 209 {3, 3, nats.MemoryStorage}, 210 {3, 3, nats.FileStorage}, 211 } 212 213 type ConsumerType string 214 const ( 215 Ephemeral ConsumerType = "Ephemeral" 216 Durable ConsumerType = "Durable" 217 ) 218 219 consumerTypeCases := []ConsumerType{ 220 Ephemeral, 221 Durable, 222 } 223 224 for _, bc := range benchmarksCases { 225 226 bName := fmt.Sprintf( 227 "N=%d,R=%d,storage=%s,C=%d", 228 bc.clusterSize, 229 bc.consumerReplicas, 230 bc.consumerStorage.String(), 231 concurrency, 232 ) 233 234 b.Run( 235 bName, 236 func(b *testing.B) { 237 238 for _, ct := range consumerTypeCases { 239 240 cName := fmt.Sprintf("Consumer=%s", ct) 241 242 b.Run( 243 cName, 244 func(b *testing.B) { 245 if verbose { 246 b.Logf( 247 "Creating %d consumers in cluster with %d nodes, R=%d, %s storage", 248 b.N, 249 bc.clusterSize, 250 bc.consumerReplicas, 251 bc.consumerStorage, 252 ) 253 } 254 255 // Setup server or cluster 256 _, leaderServer, shutdown, nc, js := startJSClusterAndConnect(b, bc.clusterSize) 257 defer shutdown() 258 defer nc.Close() 259 260 // All clients connect to cluster (meta) leader for lower variability 261 connectURL := leaderServer.ClientURL() 262 263 // Create stream 264 streamConfig := nats.StreamConfig{ 265 Name: streamName, 266 Storage: nats.FileStorage, 267 Replicas: bc.clusterSize, 268 } 269 270 _, err := js.AddStream(&streamConfig) 271 if err != nil { 272 b.Fatalf("Failed to create stream: %s", err) 273 } 274 275 // Wait for all clients and main routine to be ready 276 wgReady := sync.WaitGroup{} 277 wgReady.Add(concurrency + 1) 278 // Wait for all routines to complete 279 wgComplete := sync.WaitGroup{} 280 wgComplete.Add(concurrency) 281 282 // Number of operations (divided amongst clients) 283 opsLeft := atomic.Int64{} 284 opsLeft.Store(int64(b.N)) 285 // Total number of errors 286 totalErrors := atomic.Int64{} 287 288 // Pre-create connections and JS contexts 289 for i := 1; i <= concurrency; i++ { 290 nc, js := jsClientConnectURL(b, connectURL) 291 defer nc.Close() 292 293 go func(clientId int, nc *nats.Conn, js nats.JetStreamContext) { 294 defer wgComplete.Done() 295 296 // Config struct (reused and modified in place for each call) 297 cfg := nats.ConsumerConfig{ 298 Durable: "", 299 Name: "", 300 Replicas: bc.consumerReplicas, 301 MemoryStorage: bc.consumerStorage == nats.MemoryStorage, 302 } 303 304 // Block until everyone is ready 305 wgReady.Done() 306 wgReady.Wait() 307 308 errCount := int64(0) 309 opCount := 0 310 for opsLeft.Add(-1) >= 0 { 311 var err error 312 // Set unique consumer name 313 cfg.Name = fmt.Sprintf("%s_%d_%d", consumerPrefix, clientId, opCount) 314 if ct == Durable { 315 cfg.Durable = cfg.Name 316 } 317 _, err = js.AddConsumer(streamName, &cfg) 318 if err != nil { 319 b.Logf("Failed to add consumer: %s", err) 320 errCount += 1 321 } 322 opCount += 1 323 } 324 325 if verbose { 326 b.Logf("Client %d completed %d operations", clientId, opCount) 327 } 328 329 totalErrors.Add(errCount) 330 331 }(i, nc, js) 332 } 333 334 // Wait for all clients to be ready 335 wgReady.Done() 336 wgReady.Wait() 337 338 // Start benchmark clock 339 b.ResetTimer() 340 341 wgComplete.Wait() 342 b.StopTimer() 343 344 b.ReportMetric(float64(100*(totalErrors.Load()))/float64(b.N), "%error") 345 }, 346 ) 347 } 348 }, 349 ) 350 } 351 }