...

Source file src/cloud.google.com/go/rpcreplay/doc.go

Documentation: cloud.google.com/go/rpcreplay

     1  // Copyright 2017 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  
    15  /*
    16  Package rpcreplay supports the capture and replay of gRPC calls. Its main goal is
    17  to improve testing. Once you capture the calls of a test that runs against a real
    18  service, you have an "automatic mock" that can be replayed against the same test,
    19  yielding a unit test that is fast and flake-free.
    20  
    21  This package is EXPERIMENTAL and subject to change without notice.
    22  
    23  # Recording
    24  
    25  To record a sequence of gRPC calls to a file, create a Recorder and pass its
    26  DialOptions to grpc.Dial:
    27  
    28  	rec, err := rpcreplay.NewRecorder("service.replay", nil)
    29  	if err != nil { ... }
    30  	defer func() {
    31  	    if err := rec.Close(); err != nil { ... }
    32  	}()
    33  	conn, err := grpc.Dial(serverAddress, rec.DialOptions()...)
    34  
    35  It is essential to close the Recorder when the interaction is finished.
    36  
    37  There is also a NewRecorderWriter function for capturing to an arbitrary
    38  io.Writer.
    39  
    40  # Replaying
    41  
    42  Replaying a captured file looks almost identical: create a Replayer and use
    43  its DialOptions. (Since we're reading the file and not writing it, we don't
    44  have to be as careful about the error returned from Close).
    45  
    46  	rep, err := rpcreplay.NewReplayer("service.replay")
    47  	if err != nil { ... }
    48  	defer rep.Close()
    49  	conn, err := grpc.Dial(serverAddress, rep.DialOptions()...)
    50  
    51  Since a real connection isn't necessary for replay, you can get a fake
    52  one from the replayer instead of calling grpc.Dial:
    53  
    54  	rep, err := rpcreplay.NewReplayer("service.replay")
    55  	if err != nil { ... }
    56  	defer rep.Close()
    57  	conn, err := rep.Connection()
    58  
    59  # Initial State
    60  
    61  A test might use random or time-sensitive values, for instance to create unique
    62  resources for isolation from other tests. The test therefore has initial values, such
    63  as the current time, or a random seed, that differ from run to run. You must record
    64  this initial state and re-establish it on replay.
    65  
    66  To record the initial state, serialize it into a []byte and pass it as the second
    67  argument to NewRecorder:
    68  
    69  	timeNow := time.Now()
    70  	b, err := timeNow.MarshalBinary()
    71  	if err != nil { ... }
    72  	rec, err := rpcreplay.NewRecorder("service.replay", b)
    73  
    74  On replay, get the bytes from Replayer.Initial:
    75  
    76  	rep, err := rpcreplay.NewReplayer("service.replay")
    77  	if err != nil { ... }
    78  	defer rep.Close()
    79  	err = timeNow.UnmarshalBinary(rep.Initial())
    80  	if err != nil { ... }
    81  
    82  # Callbacks
    83  
    84  Recorders and replayers have support for running callbacks before messages are
    85  written to or read from the replay file. A Recorder has a BeforeFunc that can modify
    86  a request or response before it is written to the replay file. The actual RPCs sent
    87  to the service during recording remain unaltered; only what is saved in the replay
    88  file can be changed. A Replayer has a BeforeFunc that can modify a request before it
    89  is sent for matching.
    90  
    91  Example uses for these callbacks include customized logging, or scrubbing data before
    92  RPCs are written to the replay file. If requests are modified by the callbacks during
    93  recording, it is important to perform the same modifications to the requests when
    94  replaying, or RPC matching on replay will fail.
    95  
    96  A common way to analyze and modify the various messages is to use a type switch.
    97  
    98  	// Assume these types implement proto.Message.
    99  	type Greeting struct {
   100  		line string
   101  	}
   102  
   103  	type Farewell struct {
   104  		line string
   105  	}
   106  
   107  	func sayings(method string, msg proto.Message) error {
   108  		switch m := msg.(type) {
   109  		case Greeting:
   110  			msg.line = "Hi!"
   111  			return nil
   112  		case Farewell:
   113  			msg.line = "Bye bye!"
   114  			return nil
   115  		default:
   116  			return fmt.Errorf("unknown message type")
   117  		}
   118  	}
   119  
   120  # Nondeterminism
   121  
   122  A nondeterministic program may invoke RPCs in a different order each time
   123  it is run. The order in which RPCs are called during recording may differ
   124  from the order during replay.
   125  
   126  The replayer matches incoming to recorded requests by method name and request
   127  contents, so nondeterminism is only a concern for identical requests that result
   128  in different responses. A nondeterministic program whose behavior differs
   129  depending on the order of such RPCs probably has a race condition: since both the
   130  recorded sequence of RPCs and the sequence during replay are valid orderings, the
   131  program should behave the same under both.
   132  
   133  The same is not true of streaming RPCs. The replayer matches streams only by method
   134  name, since it has no other information at the time the stream is opened. Two streams
   135  with the same method name that are started concurrently may replay in the wrong
   136  order.
   137  
   138  # Other Replayer Differences
   139  
   140  Besides the differences in replay mentioned above, other differences may cause issues
   141  for some programs. We list them here.
   142  
   143  The Replayer delivers a response to an RPC immediately, without waiting for other
   144  incoming RPCs. This can violate causality. For example, in a Pub/Sub program where
   145  one goroutine publishes and another subscribes, during replay the Subscribe call may
   146  finish before the Publish call begins.
   147  
   148  For streaming RPCs, the Replayer delivers the result of Send and Recv calls in
   149  the order they were recorded. No attempt is made to match message contents.
   150  
   151  At present, this package does not record or replay stream headers and trailers, or
   152  the result of the CloseSend method.
   153  */
   154  package rpcreplay // import "cloud.google.com/go/rpcreplay"
   155  

View as plain text