Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 

235 řádky
6.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 pubsub
  15. import (
  16. "bytes"
  17. "fmt"
  18. "log"
  19. "math/rand"
  20. "os"
  21. "sync"
  22. "testing"
  23. "time"
  24. "golang.org/x/net/context"
  25. "cloud.google.com/go/internal/testutil"
  26. "google.golang.org/api/option"
  27. )
  28. const (
  29. timeout = time.Minute * 10
  30. ackDeadline = time.Second * 10
  31. nMessages = 1e4
  32. acceptableDupPercentage = .05
  33. numAcceptableDups = int(nMessages * acceptableDupPercentage / 100)
  34. )
  35. // Buffer log messages to debug failures.
  36. var logBuf bytes.Buffer
  37. // TestEndToEnd pumps many messages into a topic and tests that they are all
  38. // delivered to each subscription for the topic. It also tests that messages
  39. // are not unexpectedly redelivered.
  40. func TestEndToEnd(t *testing.T) {
  41. t.Parallel()
  42. if testing.Short() {
  43. t.Skip("Integration tests skipped in short mode")
  44. }
  45. log.SetOutput(&logBuf)
  46. ctx := context.Background()
  47. ts := testutil.TokenSource(ctx, ScopePubSub, ScopeCloudPlatform)
  48. if ts == nil {
  49. t.Skip("Integration tests skipped. See CONTRIBUTING.md for details")
  50. }
  51. now := time.Now()
  52. topicName := fmt.Sprintf("endtoend-%d", now.UnixNano())
  53. subPrefix := fmt.Sprintf("endtoend-%d", now.UnixNano())
  54. client, err := NewClient(ctx, testutil.ProjID(), option.WithTokenSource(ts))
  55. if err != nil {
  56. t.Fatalf("Creating client error: %v", err)
  57. }
  58. var topic *Topic
  59. if topic, err = client.CreateTopic(ctx, topicName); err != nil {
  60. t.Fatalf("CreateTopic error: %v", err)
  61. }
  62. defer topic.Delete(ctx)
  63. // Two subscriptions to the same topic.
  64. var subs [2]*Subscription
  65. for i := 0; i < len(subs); i++ {
  66. subs[i], err = client.CreateSubscription(ctx, fmt.Sprintf("%s-%d", subPrefix, i), SubscriptionConfig{
  67. Topic: topic,
  68. AckDeadline: ackDeadline,
  69. })
  70. if err != nil {
  71. t.Fatalf("CreateSub error: %v", err)
  72. }
  73. defer subs[i].Delete(ctx)
  74. }
  75. err = publish(ctx, topic, nMessages)
  76. topic.Stop()
  77. if err != nil {
  78. t.Fatalf("publish: %v", err)
  79. }
  80. // recv provides an indication that messages are still arriving.
  81. recv := make(chan struct{})
  82. // We have two subscriptions to our topic.
  83. // Each subscription will get a copy of each published message.
  84. var wg sync.WaitGroup
  85. cctx, cancel := context.WithTimeout(ctx, timeout)
  86. defer cancel()
  87. consumers := []*consumer{
  88. {counts: make(map[string]int), recv: recv, durations: []time.Duration{time.Hour}},
  89. {counts: make(map[string]int), recv: recv,
  90. durations: []time.Duration{ackDeadline, ackDeadline, ackDeadline / 2, ackDeadline / 2, time.Hour}},
  91. }
  92. for i, con := range consumers {
  93. con := con
  94. sub := subs[i]
  95. wg.Add(1)
  96. go func() {
  97. defer wg.Done()
  98. con.consume(t, cctx, sub)
  99. }()
  100. }
  101. // Wait for a while after the last message before declaring quiescence.
  102. // We wait a multiple of the ack deadline, for two reasons:
  103. // 1. To detect if messages are redelivered after having their ack
  104. // deadline extended.
  105. // 2. To wait for redelivery of messages that were en route when a Receive
  106. // is canceled. This can take considerably longer than the ack deadline.
  107. quiescenceDur := ackDeadline * 6
  108. quiescenceTimer := time.NewTimer(quiescenceDur)
  109. loop:
  110. for {
  111. select {
  112. case <-recv:
  113. // Reset timer so we wait quiescenceDur after the last message.
  114. // See https://godoc.org/time#Timer.Reset for why the Stop
  115. // and channel drain are necessary.
  116. if !quiescenceTimer.Stop() {
  117. <-quiescenceTimer.C
  118. }
  119. quiescenceTimer.Reset(quiescenceDur)
  120. case <-quiescenceTimer.C:
  121. cancel()
  122. log.Println("quiesced")
  123. break loop
  124. case <-cctx.Done():
  125. t.Fatal("timed out")
  126. }
  127. }
  128. wg.Wait()
  129. ok := true
  130. for i, con := range consumers {
  131. var numDups int
  132. var zeroes int
  133. for _, v := range con.counts {
  134. if v == 0 {
  135. zeroes += 1
  136. }
  137. numDups += v - 1
  138. }
  139. if zeroes > 0 {
  140. t.Errorf("Consumer %d: %d messages never arrived", i, zeroes)
  141. ok = false
  142. } else if numDups > numAcceptableDups {
  143. t.Errorf("Consumer %d: Willing to accept %d dups (%f%% duplicated of %d messages), but got %d", i, numAcceptableDups, acceptableDupPercentage, int(nMessages), numDups)
  144. ok = false
  145. }
  146. }
  147. if !ok {
  148. logBuf.WriteTo(os.Stdout)
  149. }
  150. }
  151. // publish publishes n messages to topic, and returns the published message IDs.
  152. func publish(ctx context.Context, topic *Topic, n int) error {
  153. var rs []*PublishResult
  154. for i := 0; i < n; i++ {
  155. m := &Message{Data: []byte(fmt.Sprintf("msg %d", i))}
  156. rs = append(rs, topic.Publish(ctx, m))
  157. }
  158. var ids []string
  159. for _, r := range rs {
  160. id, err := r.Get(ctx)
  161. if err != nil {
  162. return err
  163. }
  164. ids = append(ids, id)
  165. }
  166. return nil
  167. }
  168. // consumer consumes messages according to its configuration.
  169. type consumer struct {
  170. durations []time.Duration
  171. // A value is sent to recv each time Inc is called.
  172. recv chan struct{}
  173. mu sync.Mutex
  174. counts map[string]int
  175. total int
  176. }
  177. // consume reads messages from a subscription, and keeps track of what it receives in mc.
  178. // After consume returns, the caller should wait on wg to ensure that no more updates to mc will be made.
  179. func (c *consumer) consume(t *testing.T, ctx context.Context, sub *Subscription) {
  180. for _, dur := range c.durations {
  181. ctx2, cancel := context.WithTimeout(ctx, dur)
  182. defer cancel()
  183. id := sub.name[len(sub.name)-1:]
  184. log.Printf("%s: start receive", id)
  185. prev := c.total
  186. err := sub.Receive(ctx2, c.process)
  187. log.Printf("%s: end receive; read %d", id, c.total-prev)
  188. if err != nil {
  189. t.Errorf("error from Receive: %v", err)
  190. return
  191. }
  192. select {
  193. case <-ctx.Done():
  194. return
  195. default:
  196. }
  197. }
  198. }
  199. // process handles a message and records it in mc.
  200. func (c *consumer) process(_ context.Context, m *Message) {
  201. c.mu.Lock()
  202. c.counts[m.ID] += 1
  203. c.total++
  204. c.mu.Unlock()
  205. c.recv <- struct{}{}
  206. // Simulate time taken to process m, while continuing to process more messages.
  207. // Some messages will need to have their ack deadline extended due to this delay.
  208. delay := rand.Intn(int(ackDeadline * 3))
  209. time.AfterFunc(time.Duration(delay), m.Ack)
  210. }