vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletmanager/vreplication/queryhistory/verifier.go (about) 1 package queryhistory 2 3 import ( 4 "fmt" 5 ) 6 7 type Result struct { 8 Accepted bool 9 Error error 10 Expectation Expectation 11 Index int 12 Matched bool 13 Message string 14 } 15 16 // Verifier verifies that an actual history of queries matches an expected 17 // sequence of queries. 18 type Verifier struct { 19 matched []SequencedExpectation 20 pending map[SequencedExpectation]bool 21 history History 22 results []*Result 23 sequence ExpectationSequence 24 } 25 26 func NewVerifier(sequence ExpectationSequence) *Verifier { 27 matched := make([]SequencedExpectation, 0) 28 pending := make(map[SequencedExpectation]bool) 29 sequence.Visit(func(e SequencedExpectation) VisitControl { 30 pending[e] = true 31 return VisitContinue 32 }) 33 34 return &Verifier{ 35 matched: matched, 36 pending: pending, 37 history: make([]string, 0), 38 results: make([]*Result, 0), 39 sequence: sequence, 40 } 41 } 42 43 // AcceptQuery verifies that the provided query is valid according to the 44 // internal ExpectationSequence and the internal History of preceeding queries. 45 // Returns a *Result indicating whether the query was accepted and, if not, 46 // diagnostic details indicating why not. 47 func (v *Verifier) AcceptQuery(query string) *Result { 48 history := append(v.history, query) 49 index := len(history) - 1 50 result := &Result{ 51 Accepted: false, 52 Error: nil, 53 Expectation: nil, 54 Index: index, 55 Matched: false, 56 Message: "query did not match any expectations", 57 } 58 59 // Evaluate query against pending expectations. 60 for e, p := range v.pending { 61 // Continue to next expectation if no longer pending. 62 if !p { 63 continue 64 } 65 66 if v.checkQueryAgainstExpectation(query, e, result) { 67 // Accept query into history. 68 v.history = history 69 v.matched = append(v.matched, e) 70 v.pending[e] = false 71 v.results = append(v.results, result) 72 break 73 } 74 } 75 76 return result 77 } 78 79 // History returns the internal History of accepted queries. 80 func (v *Verifier) History() History { 81 return v.history 82 } 83 84 // Pending returns a list of Expectations that have not yet fully matched an 85 // accepted query. 86 func (v *Verifier) Pending() []Expectation { 87 pending := make([]Expectation, 0) 88 for e, p := range v.pending { 89 if p { 90 pending = append(pending, e) 91 } 92 } 93 return pending 94 } 95 96 func (v *Verifier) checkQueryAgainstExpectation(query string, expectation SequencedExpectation, result *Result) bool { 97 // Check if query matches expectation query pattern. 98 ok, err := expectation.MatchQuery(query) 99 if !ok { 100 if err != nil { 101 result.Expectation = expectation 102 result.Error = err 103 result.Message = "got an error while matching query to expectation" 104 return false 105 } 106 // Query did not match, continue to next expectation. 107 return false 108 } 109 110 // Query matched expectation query pattern. 111 result.Message = "matched an expected query" 112 result.Expectation = expectation 113 114 // See if it matches sequence expectations. 115 if expectation.ImmediatelyAfter() != nil { 116 if len(v.matched) == 0 { 117 result.Message = fmt.Sprintf( 118 "%q expected immediately after %q, but it is first", 119 query, expectation.ImmediatelyAfter().Query(), 120 ) 121 return false 122 } 123 if v.pending[expectation.ImmediatelyAfter()] { 124 result.Message = fmt.Sprintf( 125 "expected immediately after %q which is not yet matched", 126 expectation.ImmediatelyAfter().Query(), 127 ) 128 return false 129 } 130 } 131 for _, ea := range expectation.EventuallyAfter().Slice() { 132 if v.pending[ea] { 133 result.Message = fmt.Sprintf( 134 "expect eventually after %q which is not yet matched", 135 ea.Query(), 136 ) 137 return false 138 } 139 } 140 if expectation.ImmediatelyBefore() != nil { 141 if !v.pending[expectation.ImmediatelyBefore()] { 142 result.Message = fmt.Sprintf( 143 "expected immediately before %q which is already matched", 144 expectation.ImmediatelyBefore().Query(), 145 ) 146 return false 147 } 148 } 149 for _, ea := range expectation.EventuallyBefore().Slice() { 150 if !v.pending[ea] { 151 result.Message = fmt.Sprintf( 152 "expect eventually before %q which is already matched", 153 ea.Query(), 154 ) 155 return false 156 } 157 } 158 159 // Query passed expectation. 160 result.Accepted = true 161 result.Matched = true 162 result.Message = "matched expectated query and expected order" 163 164 return true 165 }