You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

201 lines
5.9 KiB

  1. /*
  2. Copyright 2017 Google LLC
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package spanner
  14. import (
  15. "context"
  16. "fmt"
  17. "strings"
  18. "time"
  19. "cloud.google.com/go/spanner/internal/backoff"
  20. "github.com/golang/protobuf/proto"
  21. "github.com/golang/protobuf/ptypes"
  22. edpb "google.golang.org/genproto/googleapis/rpc/errdetails"
  23. "google.golang.org/grpc/codes"
  24. "google.golang.org/grpc/metadata"
  25. )
  26. const (
  27. retryInfoKey = "google.rpc.retryinfo-bin"
  28. )
  29. // errRetry returns an unavailable error under error namespace EsOther. It is a
  30. // generic retryable error that is used to mask and recover unretryable errors
  31. // in a retry loop.
  32. func errRetry(err error) error {
  33. if se, ok := err.(*Error); ok {
  34. return &Error{codes.Unavailable, fmt.Sprintf("generic Cloud Spanner retryable error: { %v }", se.Error()), se.trailers}
  35. }
  36. return spannerErrorf(codes.Unavailable, "generic Cloud Spanner retryable error: { %v }", err.Error())
  37. }
  38. // isErrorClosing reports whether the error is generated by gRPC layer talking to a closed server.
  39. func isErrorClosing(err error) bool {
  40. if err == nil {
  41. return false
  42. }
  43. if ErrCode(err) == codes.Internal && strings.Contains(ErrDesc(err), "transport is closing") {
  44. // Handle the case when connection is closed unexpectedly.
  45. // TODO: once gRPC is able to categorize
  46. // this as retryable error, we should stop parsing the
  47. // error message here.
  48. return true
  49. }
  50. return false
  51. }
  52. // isErrorRST reports whether the error is generated by gRPC client receiving a RST frame from server.
  53. func isErrorRST(err error) bool {
  54. if err == nil {
  55. return false
  56. }
  57. if ErrCode(err) == codes.Internal && strings.Contains(ErrDesc(err), "stream terminated by RST_STREAM") {
  58. // TODO: once gRPC is able to categorize this error as "go away" or "retryable",
  59. // we should stop parsing the error message.
  60. return true
  61. }
  62. return false
  63. }
  64. // isErrorUnexpectedEOF returns true if error is generated by gRPC layer receiving io.EOF unexpectedly.
  65. func isErrorUnexpectedEOF(err error) bool {
  66. if err == nil {
  67. return false
  68. }
  69. // Unexpected EOF is a transport layer issue that could be recovered by
  70. // retries. The most likely scenario is a flaky RecvMsg() call due to
  71. // network issues.
  72. // For grpc version >= 1.14.0, the error code is Internal.
  73. // (https://github.com/grpc/grpc-go/releases/tag/v1.14.0)
  74. if ErrCode(err) == codes.Internal && strings.Contains(ErrDesc(err), "unexpected EOF") {
  75. return true
  76. }
  77. // For grpc version < 1.14.0, the error code in Unknown.
  78. if ErrCode(err) == codes.Unknown && strings.Contains(ErrDesc(err), "unexpected EOF") {
  79. return true
  80. }
  81. return false
  82. }
  83. // isErrorUnavailable returns true if the error is about server being unavailable.
  84. func isErrorUnavailable(err error) bool {
  85. if err == nil {
  86. return false
  87. }
  88. if ErrCode(err) == codes.Unavailable {
  89. return true
  90. }
  91. return false
  92. }
  93. // isRetryable returns true if the Cloud Spanner error being checked is a retryable error.
  94. func isRetryable(err error) bool {
  95. if isErrorClosing(err) {
  96. return true
  97. }
  98. if isErrorUnexpectedEOF(err) {
  99. return true
  100. }
  101. if isErrorRST(err) {
  102. return true
  103. }
  104. if isErrorUnavailable(err) {
  105. return true
  106. }
  107. return false
  108. }
  109. // errContextCanceled returns *spanner.Error for canceled context.
  110. func errContextCanceled(ctx context.Context, lastErr error) error {
  111. if ctx.Err() == context.DeadlineExceeded {
  112. return spannerErrorf(codes.DeadlineExceeded, "%v, lastErr is <%v>", ctx.Err(), lastErr)
  113. }
  114. return spannerErrorf(codes.Canceled, "%v, lastErr is <%v>", ctx.Err(), lastErr)
  115. }
  116. // extractRetryDelay extracts retry backoff if present.
  117. func extractRetryDelay(err error) (time.Duration, bool) {
  118. trailers := errTrailers(err)
  119. if trailers == nil {
  120. return 0, false
  121. }
  122. elem, ok := trailers[retryInfoKey]
  123. if !ok || len(elem) <= 0 {
  124. return 0, false
  125. }
  126. _, b, err := metadata.DecodeKeyValue(retryInfoKey, elem[0])
  127. if err != nil {
  128. return 0, false
  129. }
  130. var retryInfo edpb.RetryInfo
  131. if proto.Unmarshal([]byte(b), &retryInfo) != nil {
  132. return 0, false
  133. }
  134. delay, err := ptypes.Duration(retryInfo.RetryDelay)
  135. if err != nil {
  136. return 0, false
  137. }
  138. return delay, true
  139. }
  140. // runRetryable keeps attempting to run f until one of the following happens:
  141. // 1) f returns nil error or an unretryable error;
  142. // 2) context is cancelled or timeout.
  143. // TODO: consider using https://github.com/googleapis/gax-go/v2 once it
  144. // becomes available internally.
  145. func runRetryable(ctx context.Context, f func(context.Context) error) error {
  146. return toSpannerError(runRetryableNoWrap(ctx, f))
  147. }
  148. // Like runRetryable, but doesn't wrap the returned error in a spanner.Error.
  149. func runRetryableNoWrap(ctx context.Context, f func(context.Context) error) error {
  150. var funcErr error
  151. retryCount := 0
  152. for {
  153. select {
  154. case <-ctx.Done():
  155. // Do context check here so that even f() failed to do
  156. // so (for example, gRPC implementation bug), the loop
  157. // can still have a chance to exit as expected.
  158. return errContextCanceled(ctx, funcErr)
  159. default:
  160. }
  161. funcErr = f(ctx)
  162. if funcErr == nil {
  163. return nil
  164. }
  165. if isRetryable(funcErr) {
  166. // Error is retryable, do exponential backoff and continue.
  167. b, ok := extractRetryDelay(funcErr)
  168. if !ok {
  169. b = backoff.DefaultBackoff.Delay(retryCount)
  170. }
  171. statsPrintf(ctx, nil, "Backing off for %s, then retrying", b)
  172. select {
  173. case <-ctx.Done():
  174. return errContextCanceled(ctx, funcErr)
  175. case <-time.After(b):
  176. }
  177. retryCount++
  178. continue
  179. }
  180. // Error isn't retryable / no error, return immediately.
  181. return funcErr
  182. }
  183. }