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.
 
 
 

382 lines
12 KiB

  1. /*
  2. Copyright 2016 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 bigtable
  14. import (
  15. "context"
  16. "strings"
  17. "testing"
  18. "time"
  19. "cloud.google.com/go/bigtable/bttest"
  20. "cloud.google.com/go/bigtable/internal/gax"
  21. "cloud.google.com/go/internal/testutil"
  22. "github.com/golang/protobuf/ptypes/wrappers"
  23. "github.com/google/go-cmp/cmp"
  24. "google.golang.org/api/option"
  25. btpb "google.golang.org/genproto/googleapis/bigtable/v2"
  26. rpcpb "google.golang.org/genproto/googleapis/rpc/status"
  27. "google.golang.org/grpc"
  28. "google.golang.org/grpc/codes"
  29. "google.golang.org/grpc/status"
  30. )
  31. func setupFakeServer(opt ...grpc.ServerOption) (tbl *Table, cleanup func(), err error) {
  32. srv, err := bttest.NewServer("localhost:0", opt...)
  33. if err != nil {
  34. return nil, nil, err
  35. }
  36. conn, err := grpc.Dial(srv.Addr, grpc.WithInsecure(), grpc.WithBlock())
  37. if err != nil {
  38. return nil, nil, err
  39. }
  40. client, err := NewClient(context.Background(), "client", "instance", option.WithGRPCConn(conn), option.WithGRPCDialOption(grpc.WithBlock()))
  41. if err != nil {
  42. return nil, nil, err
  43. }
  44. adminClient, err := NewAdminClient(context.Background(), "client", "instance", option.WithGRPCConn(conn), option.WithGRPCDialOption(grpc.WithBlock()))
  45. if err != nil {
  46. return nil, nil, err
  47. }
  48. if err := adminClient.CreateTable(context.Background(), "table"); err != nil {
  49. return nil, nil, err
  50. }
  51. if err := adminClient.CreateColumnFamily(context.Background(), "table", "cf"); err != nil {
  52. return nil, nil, err
  53. }
  54. t := client.Open("table")
  55. cleanupFunc := func() {
  56. adminClient.Close()
  57. client.Close()
  58. srv.Close()
  59. }
  60. return t, cleanupFunc, nil
  61. }
  62. func TestRetryApply(t *testing.T) {
  63. gax.Logger = nil
  64. ctx := context.Background()
  65. errCount := 0
  66. code := codes.Unavailable // Will be retried
  67. // Intercept requests and return an error or defer to the underlying handler
  68. errInjector := func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
  69. if strings.HasSuffix(info.FullMethod, "MutateRow") && errCount < 3 {
  70. errCount++
  71. return nil, status.Errorf(code, "")
  72. }
  73. return handler(ctx, req)
  74. }
  75. tbl, cleanup, err := setupFakeServer(grpc.UnaryInterceptor(errInjector))
  76. if err != nil {
  77. t.Fatalf("fake server setup: %v", err)
  78. }
  79. defer cleanup()
  80. mut := NewMutation()
  81. mut.Set("cf", "col", 1000, []byte("val"))
  82. if err := tbl.Apply(ctx, "row1", mut); err != nil {
  83. t.Errorf("applying single mutation with retries: %v", err)
  84. }
  85. row, err := tbl.ReadRow(ctx, "row1")
  86. if err != nil {
  87. t.Errorf("reading single value with retries: %v", err)
  88. }
  89. if row == nil {
  90. t.Errorf("applying single mutation with retries: could not read back row")
  91. }
  92. code = codes.FailedPrecondition // Won't be retried
  93. errCount = 0
  94. if err := tbl.Apply(ctx, "row", mut); err == nil {
  95. t.Errorf("applying single mutation with no retries: no error")
  96. }
  97. // Check and mutate
  98. mutTrue := NewMutation()
  99. mutTrue.DeleteRow()
  100. mutFalse := NewMutation()
  101. mutFalse.Set("cf", "col", 1000, []byte("val"))
  102. condMut := NewCondMutation(ValueFilter(".*"), mutTrue, mutFalse)
  103. errCount = 0
  104. code = codes.Unavailable // Will be retried
  105. if err := tbl.Apply(ctx, "row1", condMut); err != nil {
  106. t.Errorf("conditionally mutating row with retries: %v", err)
  107. }
  108. row, err = tbl.ReadRow(ctx, "row1") // row1 already in the table
  109. if err != nil {
  110. t.Errorf("reading single value after conditional mutation: %v", err)
  111. }
  112. if row != nil {
  113. t.Errorf("reading single value after conditional mutation: row not deleted")
  114. }
  115. errCount = 0
  116. code = codes.FailedPrecondition // Won't be retried
  117. if err := tbl.Apply(ctx, "row", condMut); err == nil {
  118. t.Errorf("conditionally mutating row with no retries: no error")
  119. }
  120. }
  121. func TestRetryApplyBulk(t *testing.T) {
  122. ctx := context.Background()
  123. gax.Logger = nil
  124. // Intercept requests and delegate to an interceptor defined by the test case
  125. errCount := 0
  126. var f func(grpc.ServerStream) error
  127. errInjector := func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
  128. if strings.HasSuffix(info.FullMethod, "MutateRows") {
  129. return f(ss)
  130. }
  131. return handler(ctx, ss)
  132. }
  133. tbl, cleanup, err := setupFakeServer(grpc.StreamInterceptor(errInjector))
  134. defer cleanup()
  135. if err != nil {
  136. t.Fatalf("fake server setup: %v", err)
  137. }
  138. errCount = 0
  139. // Test overall request failure and retries
  140. f = func(ss grpc.ServerStream) error {
  141. if errCount < 3 {
  142. errCount++
  143. return status.Errorf(codes.Aborted, "")
  144. }
  145. return nil
  146. }
  147. mut := NewMutation()
  148. mut.Set("cf", "col", 1, []byte{})
  149. errors, err := tbl.ApplyBulk(ctx, []string{"row2"}, []*Mutation{mut})
  150. if errors != nil || err != nil {
  151. t.Errorf("bulk with request failure: got: %v, %v, want: nil", errors, err)
  152. }
  153. // Test failures and retries in one request
  154. errCount = 0
  155. m1 := NewMutation()
  156. m1.Set("cf", "col", 1, []byte{})
  157. m2 := NewMutation()
  158. m2.Set("cf", "col2", 1, []byte{})
  159. m3 := NewMutation()
  160. m3.Set("cf", "col3", 1, []byte{})
  161. f = func(ss grpc.ServerStream) error {
  162. var err error
  163. req := new(btpb.MutateRowsRequest)
  164. must(ss.RecvMsg(req))
  165. switch errCount {
  166. case 0:
  167. // Retryable request failure
  168. err = status.Errorf(codes.Unavailable, "")
  169. case 1:
  170. // Two mutations fail
  171. must(writeMutateRowsResponse(ss, codes.Unavailable, codes.OK, codes.Aborted))
  172. err = nil
  173. case 2:
  174. // Two failures were retried. One will succeed.
  175. if want, got := 2, len(req.Entries); want != got {
  176. t.Errorf("2 bulk retries, got: %d, want %d", got, want)
  177. }
  178. must(writeMutateRowsResponse(ss, codes.OK, codes.Aborted))
  179. err = nil
  180. case 3:
  181. // One failure was retried and will succeed.
  182. if want, got := 1, len(req.Entries); want != got {
  183. t.Errorf("1 bulk retry, got: %d, want %d", got, want)
  184. }
  185. must(writeMutateRowsResponse(ss, codes.OK))
  186. err = nil
  187. }
  188. errCount++
  189. return err
  190. }
  191. errors, err = tbl.ApplyBulk(ctx, []string{"row1", "row2", "row3"}, []*Mutation{m1, m2, m3})
  192. if errors != nil || err != nil {
  193. t.Errorf("bulk with retries: got: %v, %v, want: nil", errors, err)
  194. }
  195. // Test unretryable errors
  196. niMut := NewMutation()
  197. niMut.Set("cf", "col", ServerTime, []byte{}) // Non-idempotent
  198. errCount = 0
  199. f = func(ss grpc.ServerStream) error {
  200. var err error
  201. req := new(btpb.MutateRowsRequest)
  202. must(ss.RecvMsg(req))
  203. switch errCount {
  204. case 0:
  205. // Give non-idempotent mutation a retryable error code.
  206. // Nothing should be retried.
  207. must(writeMutateRowsResponse(ss, codes.FailedPrecondition, codes.Aborted))
  208. err = nil
  209. case 1:
  210. t.Errorf("unretryable errors: got one retry, want no retries")
  211. }
  212. errCount++
  213. return err
  214. }
  215. errors, err = tbl.ApplyBulk(ctx, []string{"row1", "row2"}, []*Mutation{m1, niMut})
  216. if err != nil {
  217. t.Errorf("unretryable errors: request failed %v", err)
  218. }
  219. want := []error{
  220. status.Errorf(codes.FailedPrecondition, ""),
  221. status.Errorf(codes.Aborted, ""),
  222. }
  223. if !testutil.Equal(want, errors) {
  224. t.Errorf("unretryable errors: got: %v, want: %v", errors, want)
  225. }
  226. // Test individual errors and a deadline exceeded
  227. f = func(ss grpc.ServerStream) error {
  228. return writeMutateRowsResponse(ss, codes.FailedPrecondition, codes.OK, codes.Aborted)
  229. }
  230. ctx, _ = context.WithTimeout(ctx, 100*time.Millisecond)
  231. errors, err = tbl.ApplyBulk(ctx, []string{"row1", "row2", "row3"}, []*Mutation{m1, m2, m3})
  232. wantErr := context.DeadlineExceeded
  233. if wantErr != err {
  234. t.Errorf("deadline exceeded error: got: %v, want: %v", err, wantErr)
  235. }
  236. if errors != nil {
  237. t.Errorf("deadline exceeded errors: got: %v, want: nil", err)
  238. }
  239. }
  240. func writeMutateRowsResponse(ss grpc.ServerStream, codes ...codes.Code) error {
  241. res := &btpb.MutateRowsResponse{Entries: make([]*btpb.MutateRowsResponse_Entry, len(codes))}
  242. for i, code := range codes {
  243. res.Entries[i] = &btpb.MutateRowsResponse_Entry{
  244. Index: int64(i),
  245. Status: &rpcpb.Status{Code: int32(code), Message: ""},
  246. }
  247. }
  248. return ss.SendMsg(res)
  249. }
  250. func TestRetainRowsAfter(t *testing.T) {
  251. prevRowRange := NewRange("a", "z")
  252. prevRowKey := "m"
  253. want := NewRange("m\x00", "z")
  254. got := prevRowRange.retainRowsAfter(prevRowKey)
  255. if !testutil.Equal(want, got, cmp.AllowUnexported(RowRange{})) {
  256. t.Errorf("range retry: got %v, want %v", got, want)
  257. }
  258. prevRowRangeList := RowRangeList{NewRange("a", "d"), NewRange("e", "g"), NewRange("h", "l")}
  259. prevRowKey = "f"
  260. wantRowRangeList := RowRangeList{NewRange("f\x00", "g"), NewRange("h", "l")}
  261. got = prevRowRangeList.retainRowsAfter(prevRowKey)
  262. if !testutil.Equal(wantRowRangeList, got, cmp.AllowUnexported(RowRange{})) {
  263. t.Errorf("range list retry: got %v, want %v", got, wantRowRangeList)
  264. }
  265. prevRowList := RowList{"a", "b", "c", "d", "e", "f"}
  266. prevRowKey = "b"
  267. wantList := RowList{"c", "d", "e", "f"}
  268. got = prevRowList.retainRowsAfter(prevRowKey)
  269. if !testutil.Equal(wantList, got) {
  270. t.Errorf("list retry: got %v, want %v", got, wantList)
  271. }
  272. }
  273. func TestRetryReadRows(t *testing.T) {
  274. ctx := context.Background()
  275. gax.Logger = nil
  276. // Intercept requests and delegate to an interceptor defined by the test case
  277. errCount := 0
  278. var f func(grpc.ServerStream) error
  279. errInjector := func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
  280. if strings.HasSuffix(info.FullMethod, "ReadRows") {
  281. return f(ss)
  282. }
  283. return handler(ctx, ss)
  284. }
  285. tbl, cleanup, err := setupFakeServer(grpc.StreamInterceptor(errInjector))
  286. defer cleanup()
  287. if err != nil {
  288. t.Fatalf("fake server setup: %v", err)
  289. }
  290. errCount = 0
  291. // Test overall request failure and retries
  292. f = func(ss grpc.ServerStream) error {
  293. var err error
  294. req := new(btpb.ReadRowsRequest)
  295. must(ss.RecvMsg(req))
  296. switch errCount {
  297. case 0:
  298. // Retryable request failure
  299. err = status.Errorf(codes.Unavailable, "")
  300. case 1:
  301. // Write two rows then error
  302. if want, got := "a", string(req.Rows.RowRanges[0].GetStartKeyClosed()); want != got {
  303. t.Errorf("first retry, no data received yet: got %q, want %q", got, want)
  304. }
  305. must(writeReadRowsResponse(ss, "a", "b"))
  306. err = status.Errorf(codes.Unavailable, "")
  307. case 2:
  308. // Retryable request failure
  309. if want, got := "b\x00", string(req.Rows.RowRanges[0].GetStartKeyClosed()); want != got {
  310. t.Errorf("2 range retries: got %q, want %q", got, want)
  311. }
  312. err = status.Errorf(codes.Unavailable, "")
  313. case 3:
  314. // Write two more rows
  315. must(writeReadRowsResponse(ss, "c", "d"))
  316. err = nil
  317. }
  318. errCount++
  319. return err
  320. }
  321. var got []string
  322. must(tbl.ReadRows(ctx, NewRange("a", "z"), func(r Row) bool {
  323. got = append(got, r.Key())
  324. return true
  325. }))
  326. want := []string{"a", "b", "c", "d"}
  327. if !testutil.Equal(got, want) {
  328. t.Errorf("retry range integration: got %v, want %v", got, want)
  329. }
  330. }
  331. func writeReadRowsResponse(ss grpc.ServerStream, rowKeys ...string) error {
  332. var chunks []*btpb.ReadRowsResponse_CellChunk
  333. for _, key := range rowKeys {
  334. chunks = append(chunks, &btpb.ReadRowsResponse_CellChunk{
  335. RowKey: []byte(key),
  336. FamilyName: &wrappers.StringValue{Value: "fm"},
  337. Qualifier: &wrappers.BytesValue{Value: []byte("col")},
  338. RowStatus: &btpb.ReadRowsResponse_CellChunk_CommitRow{CommitRow: true},
  339. })
  340. }
  341. return ss.SendMsg(&btpb.ReadRowsResponse{Chunks: chunks})
  342. }
  343. func must(err error) {
  344. if err != nil {
  345. panic(err)
  346. }
  347. }