github.com/nats-io/nats-server/v2@v2.11.0-preview.2/server/benchmark_publish_test.go (about) 1 // Copyright 2022 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 package server 15 16 import ( 17 "fmt" 18 "math/rand" 19 "sync/atomic" 20 "testing" 21 22 "github.com/nats-io/nats.go" 23 ) 24 25 func BenchmarkPublish(b *testing.B) { 26 27 const ( 28 verbose = false 29 seed = 12345 30 minMessages = 10_000 31 subject = "S" 32 queue = "Q" 33 ) 34 35 const ( 36 KB = 1024 37 MB = KB * KB 38 ) 39 40 type SubscriberType string 41 const ( 42 Async SubscriberType = "Async" 43 QueueAsync SubscriberType = "AsyncQueue" 44 None SubscriberType = "None" 45 ) 46 47 benchmarksCases := []struct { 48 messageSize int 49 }{ 50 {0}, 51 {1}, 52 {32}, 53 {128}, 54 {512}, 55 {4 * KB}, 56 {32 * KB}, 57 {128 * KB}, 58 {512 * KB}, 59 {1 * MB}, 60 } 61 62 // All the cases above are run for each of the subscriber cases below 63 subscribersCases := []struct { 64 numSubs int 65 subType SubscriberType 66 }{ 67 {0, None}, 68 {1, Async}, 69 {1, QueueAsync}, 70 {10, Async}, 71 {10, QueueAsync}, 72 } 73 74 for _, bc := range benchmarksCases { 75 bcName := fmt.Sprintf( 76 "MsgSz=%db", 77 bc.messageSize, 78 ) 79 80 b.Run( 81 bcName, 82 func(b *testing.B) { 83 84 for _, sc := range subscribersCases { 85 86 scName := fmt.Sprintf("Subs=%dx%v", sc.numSubs, sc.subType) 87 if sc.subType == None { 88 scName = fmt.Sprintf("Subs=%v", sc.subType) 89 } 90 91 b.Run( 92 scName, 93 func(b *testing.B) { 94 // Skip short runs, benchmark gets re-executed with a larger N 95 if b.N < minMessages { 96 b.ResetTimer() 97 return 98 } 99 100 if verbose { 101 b.Logf("Running %s/%s with %d ops", bcName, scName, b.N) 102 } 103 104 subErrors := uint64(0) 105 handleSubError := func(_ *nats.Conn, _ *nats.Subscription, _ error) { 106 atomic.AddUint64(&subErrors, 1) 107 } 108 109 // Start single server (no JS) 110 opts := DefaultTestOptions 111 opts.Port = -1 112 s := RunServer(&opts) 113 defer s.Shutdown() 114 115 // Create subscribers 116 for i := 0; i < sc.numSubs; i++ { 117 subConn, connErr := nats.Connect(s.ClientURL(), nats.ErrorHandler(handleSubError)) 118 if connErr != nil { 119 b.Fatalf("Failed to connect: %v", connErr) 120 } 121 defer subConn.Close() 122 123 var sub *nats.Subscription 124 var subErr error 125 126 switch sc.subType { 127 case None: 128 // No subscription 129 case Async: 130 sub, subErr = subConn.Subscribe(subject, func(*nats.Msg) {}) 131 case QueueAsync: 132 sub, subErr = subConn.QueueSubscribe(subject, queue, func(*nats.Msg) {}) 133 default: 134 b.Fatalf("Unknow subscribers type: %v", sc.subType) 135 } 136 137 if subErr != nil { 138 b.Fatalf("Failed to subscribe: %v", subErr) 139 } 140 defer sub.Unsubscribe() 141 // Do not drop messages due to slow subscribers: 142 sub.SetPendingLimits(-1, -1) 143 } 144 145 // Create publisher connection 146 nc, err := nats.Connect(s.ClientURL()) 147 if err != nil { 148 b.Fatalf("Failed to connect: %v", err) 149 } 150 defer nc.Close() 151 152 rng := rand.New(rand.NewSource(int64(seed))) 153 message := make([]byte, bc.messageSize) 154 var published, errors int 155 156 // Benchmark starts here 157 b.ResetTimer() 158 159 for i := 0; i < b.N; i++ { 160 rng.Read(message) 161 pubErr := nc.Publish(subject, message) 162 if pubErr != nil { 163 errors++ 164 } else { 165 published++ 166 b.SetBytes(int64(bc.messageSize)) 167 } 168 } 169 170 // Benchmark ends here 171 b.StopTimer() 172 173 if published+errors != b.N { 174 b.Fatalf("Something doesn't add up: %d + %d != %d", published, errors, b.N) 175 } else if subErrors > 0 { 176 b.Fatalf("Subscribers errors: %d", subErrors) 177 } 178 179 b.ReportMetric(float64(errors)*100/float64(b.N), "%error") 180 }, 181 ) 182 } 183 }, 184 ) 185 } 186 }