25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 

331 satır
9.4 KiB

  1. // Copyright 2014 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package longtest
  15. import (
  16. "context"
  17. "fmt"
  18. "log"
  19. "math/rand"
  20. "sync"
  21. "testing"
  22. "time"
  23. "cloud.google.com/go/internal/testutil"
  24. "cloud.google.com/go/pubsub"
  25. "google.golang.org/api/option"
  26. "google.golang.org/grpc/codes"
  27. "google.golang.org/grpc/status"
  28. )
  29. const (
  30. timeout = time.Minute * 10
  31. ackDeadline = time.Second * 10
  32. nMessages = 1e4
  33. acceptableDupPercentage = 1
  34. numAcceptableDups = int(nMessages * acceptableDupPercentage / 100)
  35. )
  36. // The end-to-end pumps many messages into a topic and tests that they are all
  37. // delivered to each subscription for the topic. It also tests that messages
  38. // are not unexpectedly redelivered.
  39. func TestEndToEnd_Dupes(t *testing.T) {
  40. ctx, cancel := context.WithTimeout(context.Background(), timeout)
  41. defer cancel()
  42. client, topic, cleanup := prepareEndToEndTest(ctx, t)
  43. defer cleanup()
  44. subPrefix := fmt.Sprintf("endtoend-%d", time.Now().UnixNano())
  45. // Two subscriptions to the same topic.
  46. var err error
  47. var subs [2]*pubsub.Subscription
  48. for i := 0; i < len(subs); i++ {
  49. subs[i], err = client.CreateSubscription(ctx, fmt.Sprintf("%s-%d", subPrefix, i), pubsub.SubscriptionConfig{
  50. Topic: topic,
  51. AckDeadline: ackDeadline,
  52. })
  53. if err != nil {
  54. t.Fatalf("CreateSub error: %v", err)
  55. }
  56. defer subs[i].Delete(ctx)
  57. }
  58. err = publish(ctx, topic, nMessages)
  59. topic.Stop()
  60. if err != nil {
  61. t.Fatalf("publish: %v", err)
  62. }
  63. // recv provides an indication that messages are still arriving.
  64. recv := make(chan struct{})
  65. // We have two subscriptions to our topic.
  66. // Each subscription will get a copy of each published message.
  67. var wg sync.WaitGroup
  68. cctx, cancel := context.WithTimeout(ctx, timeout)
  69. defer cancel()
  70. consumers := []*consumer{
  71. {counts: make(map[string]int), recv: recv, durations: []time.Duration{time.Hour}},
  72. {counts: make(map[string]int), recv: recv,
  73. durations: []time.Duration{ackDeadline, ackDeadline, ackDeadline / 2, ackDeadline / 2, time.Hour}},
  74. }
  75. for i, con := range consumers {
  76. con := con
  77. sub := subs[i]
  78. wg.Add(1)
  79. go func() {
  80. defer wg.Done()
  81. con.consume(ctx, t, sub)
  82. }()
  83. }
  84. // Wait for a while after the last message before declaring quiescence.
  85. // We wait a multiple of the ack deadline, for two reasons:
  86. // 1. To detect if messages are redelivered after having their ack
  87. // deadline extended.
  88. // 2. To wait for redelivery of messages that were en route when a Receive
  89. // is canceled. This can take considerably longer than the ack deadline.
  90. quiescenceDur := ackDeadline * 6
  91. quiescenceTimer := time.NewTimer(quiescenceDur)
  92. loop:
  93. for {
  94. select {
  95. case <-recv:
  96. // Reset timer so we wait quiescenceDur after the last message.
  97. // See https://godoc.org/time#Timer.Reset for why the Stop
  98. // and channel drain are necessary.
  99. if !quiescenceTimer.Stop() {
  100. <-quiescenceTimer.C
  101. }
  102. quiescenceTimer.Reset(quiescenceDur)
  103. case <-quiescenceTimer.C:
  104. cancel()
  105. log.Println("quiesced")
  106. break loop
  107. case <-cctx.Done():
  108. t.Fatal("timed out")
  109. }
  110. }
  111. wg.Wait()
  112. for i, con := range consumers {
  113. var numDups int
  114. var zeroes int
  115. for _, v := range con.counts {
  116. if v == 0 {
  117. zeroes++
  118. }
  119. numDups += v - 1
  120. }
  121. if zeroes > 0 {
  122. t.Errorf("Consumer %d: %d messages never arrived", i, zeroes)
  123. } else if numDups > numAcceptableDups {
  124. t.Errorf("Consumer %d: Willing to accept %d dups (%v duplicated of %d messages), but got %d", i, numAcceptableDups, acceptableDupPercentage, int(nMessages), numDups)
  125. }
  126. }
  127. }
  128. func TestEndToEnd_LongProcessingTime(t *testing.T) {
  129. ctx, cancel := context.WithTimeout(context.Background(), timeout)
  130. defer cancel()
  131. client, topic, cleanup := prepareEndToEndTest(ctx, t)
  132. defer cleanup()
  133. subPrefix := fmt.Sprintf("endtoend-%d", time.Now().UnixNano())
  134. // Two subscriptions to the same topic.
  135. sub, err := client.CreateSubscription(ctx, subPrefix+"-00", pubsub.SubscriptionConfig{
  136. Topic: topic,
  137. AckDeadline: ackDeadline,
  138. })
  139. if err != nil {
  140. t.Fatalf("CreateSub error: %v", err)
  141. }
  142. defer sub.Delete(ctx)
  143. // Tests the issue found in https://github.com/googleapis/google-cloud-go/issues/1247.
  144. sub.ReceiveSettings.Synchronous = true
  145. sub.ReceiveSettings.MaxOutstandingMessages = 500
  146. err = publish(ctx, topic, 1000)
  147. topic.Stop()
  148. if err != nil {
  149. t.Fatalf("publish: %v", err)
  150. }
  151. // recv provides an indication that messages are still arriving.
  152. recv := make(chan struct{})
  153. consumer := consumer{
  154. counts: make(map[string]int),
  155. recv: recv,
  156. durations: []time.Duration{time.Hour},
  157. processingDelay: func() time.Duration {
  158. return time.Duration(1+rand.Int63n(120)) * time.Second
  159. },
  160. }
  161. go consumer.consume(ctx, t, sub)
  162. // Wait for a while after the last message before declaring quiescence.
  163. // We wait a multiple of the ack deadline, for two reasons:
  164. // 1. To detect if messages are redelivered after having their ack
  165. // deadline extended.
  166. // 2. To wait for redelivery of messages that were en route when a Receive
  167. // is canceled. This can take considerably longer than the ack deadline.
  168. quiescenceDur := ackDeadline * 6
  169. quiescenceTimer := time.NewTimer(quiescenceDur)
  170. loop:
  171. for {
  172. select {
  173. case <-recv:
  174. // Reset timer so we wait quiescenceDur after the last message.
  175. // See https://godoc.org/time#Timer.Reset for why the Stop
  176. // and channel drain are necessary.
  177. if !quiescenceTimer.Stop() {
  178. <-quiescenceTimer.C
  179. }
  180. quiescenceTimer.Reset(quiescenceDur)
  181. case <-quiescenceTimer.C:
  182. cancel()
  183. log.Println("quiesced")
  184. break loop
  185. case <-ctx.Done():
  186. t.Fatal("timed out")
  187. }
  188. }
  189. var numDups int
  190. var zeroes int
  191. for _, v := range consumer.counts {
  192. if v == 0 {
  193. zeroes++
  194. }
  195. numDups += v - 1
  196. }
  197. if zeroes > 0 {
  198. t.Errorf("%d messages never arrived", zeroes)
  199. } else if numDups > numAcceptableDups {
  200. t.Errorf("Willing to accept %d dups (%v duplicated of %d messages), but got %d", numAcceptableDups, acceptableDupPercentage, int(nMessages), numDups)
  201. }
  202. }
  203. // publish publishes n messages to topic.
  204. func publish(ctx context.Context, topic *pubsub.Topic, n int) error {
  205. var rs []*pubsub.PublishResult
  206. for i := 0; i < n; i++ {
  207. m := &pubsub.Message{Data: []byte(fmt.Sprintf("msg %d", i))}
  208. rs = append(rs, topic.Publish(ctx, m))
  209. }
  210. for _, r := range rs {
  211. _, err := r.Get(ctx)
  212. if err != nil {
  213. return err
  214. }
  215. }
  216. return nil
  217. }
  218. // consumer consumes messages according to its configuration.
  219. type consumer struct {
  220. // A consumer will spin out a Receive for each duration, which will be
  221. // canceled after each duration and the next one spun up. For example, if
  222. // there are 5 3 second durations, then there will be 5 3 second Receives.
  223. durations []time.Duration
  224. // A value is sent to recv each time Inc is called.
  225. recv chan struct{}
  226. // How long to wait for before acking.
  227. processingDelay func() time.Duration
  228. mu sync.Mutex
  229. counts map[string]int // msgID: recvdAmt
  230. totalRecvd int
  231. }
  232. // consume reads messages from a subscription, and keeps track of what it receives in mc.
  233. // After consume returns, the caller should wait on wg to ensure that no more updates to mc will be made.
  234. func (c *consumer) consume(ctx context.Context, t *testing.T, sub *pubsub.Subscription) {
  235. for _, dur := range c.durations {
  236. ctx2, cancel := context.WithTimeout(ctx, dur)
  237. defer cancel()
  238. id := sub.String()[len(sub.String())-1:]
  239. t.Logf("%s: start receive", id)
  240. prev := c.totalRecvd
  241. err := sub.Receive(ctx2, c.process)
  242. t.Logf("%s: end receive; read %d", id, c.totalRecvd-prev)
  243. if serr, _ := status.FromError(err); err != nil && serr.Code() != codes.Canceled {
  244. panic(err)
  245. }
  246. select {
  247. case <-ctx.Done():
  248. return
  249. default:
  250. }
  251. }
  252. }
  253. // process handles a message and records it in mc.
  254. func (c *consumer) process(_ context.Context, m *pubsub.Message) {
  255. c.mu.Lock()
  256. c.counts[m.ID]++
  257. c.totalRecvd++
  258. c.mu.Unlock()
  259. c.recv <- struct{}{}
  260. var delay time.Duration
  261. if c.processingDelay == nil {
  262. delay = time.Duration(rand.Intn(int(ackDeadline * 3)))
  263. } else {
  264. delay = c.processingDelay()
  265. }
  266. // Simulate time taken to process m, while continuing to process more messages.
  267. // Some messages will need to have their ack deadline extended due to this delay.
  268. time.AfterFunc(delay, func() {
  269. m.Ack()
  270. })
  271. }
  272. // Remember to call cleanup!
  273. func prepareEndToEndTest(ctx context.Context, t *testing.T) (*pubsub.Client, *pubsub.Topic, func()) {
  274. if testing.Short() {
  275. t.Skip("Integration tests skipped in short mode")
  276. }
  277. ts := testutil.TokenSource(ctx, pubsub.ScopePubSub, pubsub.ScopeCloudPlatform)
  278. if ts == nil {
  279. t.Skip("Integration tests skipped. See CONTRIBUTING.md for details")
  280. }
  281. now := time.Now()
  282. topicName := fmt.Sprintf("endtoend-%d", now.UnixNano())
  283. client, err := pubsub.NewClient(ctx, testutil.ProjID(), option.WithTokenSource(ts))
  284. if err != nil {
  285. t.Fatalf("Creating client error: %v", err)
  286. }
  287. var topic *pubsub.Topic
  288. if topic, err = client.CreateTopic(ctx, topicName); err != nil {
  289. t.Fatalf("CreateTopic error: %v", err)
  290. }
  291. return client, topic, func() {
  292. topic.Delete(ctx)
  293. client.Close()
  294. }
  295. }