github.com/SaurabhDubey-Groww/go-cloud@v0.0.0-20221124105541-b26c29285fd8/docstore/driver/util.go (about) 1 // Copyright 2019 The Go Cloud Development Kit Authors 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 // https://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 package driver 16 17 import ( 18 "reflect" 19 "sort" 20 "sync" 21 22 "github.com/google/uuid" 23 ) 24 25 // UniqueString generates a string that is unique with high probability. 26 // Driver implementations can use it to generate keys for Create actions. 27 func UniqueString() string { return uuid.New().String() } 28 29 // SplitActions divides the actions slice into sub-slices much like strings.Split. 30 // The split function should report whether two consecutive actions should be split, 31 // that is, should be in different sub-slices. The first argument to split is the 32 // last action of the sub-slice currently under construction; the second argument is 33 // the action being considered for addition to that sub-slice. 34 // SplitActions doesn't change the order of the input slice. 35 func SplitActions(actions []*Action, split func(a, b *Action) bool) [][]*Action { 36 var ( 37 groups [][]*Action // the actions, split; the return value 38 cur []*Action // the group currently being constructed 39 ) 40 collect := func() { // called when the current group is known to be finished 41 if len(cur) > 0 { 42 groups = append(groups, cur) 43 cur = nil 44 } 45 } 46 for _, a := range actions { 47 if len(cur) > 0 && split(cur[len(cur)-1], a) { 48 collect() 49 } 50 cur = append(cur, a) 51 } 52 collect() 53 return groups 54 } 55 56 // GroupActions separates actions into four sets: writes, gets that must happen before the writes, 57 // gets that must happen after the writes, and gets that can happen concurrently with the writes. 58 func GroupActions(actions []*Action) (beforeGets, getList, writeList, afterGets []*Action) { 59 // maps from key to action 60 bgets := map[interface{}]*Action{} 61 agets := map[interface{}]*Action{} 62 cgets := map[interface{}]*Action{} 63 writes := map[interface{}]*Action{} 64 var nilkeys []*Action 65 for _, a := range actions { 66 if a.Key == nil { 67 // Probably a Create. 68 nilkeys = append(nilkeys, a) 69 } else if a.Kind == Get { 70 // If there was a prior write with this key, make sure this get 71 // happens after the writes. 72 if _, ok := writes[a.Key]; ok { 73 agets[a.Key] = a 74 } else { 75 cgets[a.Key] = a 76 } 77 } else { 78 // This is a write. A prior get on the same key was put into cgets; move 79 // it to bgets because it has to happen before writes. 80 if g, ok := cgets[a.Key]; ok { 81 delete(cgets, a.Key) 82 bgets[a.Key] = g 83 } 84 writes[a.Key] = a 85 } 86 } 87 88 vals := func(m map[interface{}]*Action) []*Action { 89 var as []*Action 90 for _, v := range m { 91 as = append(as, v) 92 } 93 // Sort so the order is always the same for replay. 94 sort.Slice(as, func(i, j int) bool { return as[i].Index < as[j].Index }) 95 return as 96 } 97 98 return vals(bgets), vals(cgets), append(vals(writes), nilkeys...), vals(agets) 99 } 100 101 // AsFunc creates and returns an "as function" that behaves as follows: 102 // If its argument is a pointer to the same type as val, the argument is set to val 103 // and the function returns true. Otherwise, the function returns false. 104 func AsFunc(val interface{}) func(interface{}) bool { 105 rval := reflect.ValueOf(val) 106 wantType := reflect.PtrTo(rval.Type()) 107 return func(i interface{}) bool { 108 if i == nil { 109 return false 110 } 111 ri := reflect.ValueOf(i) 112 if ri.Type() != wantType { 113 return false 114 } 115 ri.Elem().Set(rval) 116 return true 117 } 118 } 119 120 // GroupByFieldPath collect the Get actions into groups with the same set of 121 // field paths. 122 func GroupByFieldPath(gets []*Action) [][]*Action { 123 // This is quadratic in the worst case, but it's unlikely that there would be 124 // many Gets with different field paths. 125 var groups [][]*Action 126 seen := map[*Action]bool{} 127 for len(seen) < len(gets) { 128 var g []*Action 129 for _, a := range gets { 130 if !seen[a] { 131 if len(g) == 0 || fpsEqual(g[0].FieldPaths, a.FieldPaths) { 132 g = append(g, a) 133 seen[a] = true 134 } 135 } 136 } 137 groups = append(groups, g) 138 } 139 return groups 140 } 141 142 // Report whether two lists of field paths are equal. 143 func fpsEqual(fps1, fps2 [][]string) bool { 144 // TODO?: We really care about sets of field paths, but that's too tedious to determine. 145 if len(fps1) != len(fps2) { 146 return false 147 } 148 for i, fp1 := range fps1 { 149 if !FieldPathsEqual(fp1, fps2[i]) { 150 return false 151 } 152 } 153 return true 154 } 155 156 // FieldPathsEqual reports whether two field paths are equal. 157 func FieldPathsEqual(fp1, fp2 []string) bool { 158 if len(fp1) != len(fp2) { 159 return false 160 } 161 for i, s1 := range fp1 { 162 if s1 != fp2[i] { 163 return false 164 } 165 } 166 return true 167 } 168 169 // FieldPathEqualsField reports whether a field path equals a field. 170 // This is a convenience for FieldPathsEqual(fp, []string{s}). 171 func FieldPathEqualsField(fp []string, s string) bool { 172 return len(fp) == 1 && fp[0] == s 173 } 174 175 // Throttle is used to limit the number of outstanding activities, like RPCs. 176 // It acts like a combination of a semaphore and a WaitGroup. 177 type Throttle struct { 178 c chan struct{} // token semaphore 179 wg sync.WaitGroup 180 } 181 182 // NewThrottle returns a Throttle that will allow max calls to Acquire that 183 // are not matched with Release calls before blocking. 184 // If max is <= 0, there is no throttling: Acquire always returns immediately. 185 func NewThrottle(max int) *Throttle { 186 t := &Throttle{} 187 if max > 0 { 188 t.c = make(chan struct{}, max) 189 } 190 return t 191 } 192 193 // Acquire blocks until a token is available, then acquires it and returns. 194 // Acquire is deliberately not sensitive to context.Context, because it assumes 195 // that whatever acquires a token will be context-sensitive, and thus will release 196 // the token when the context is done. 197 func (t *Throttle) Acquire() { 198 t.wg.Add(1) 199 if t.c != nil { 200 t.c <- struct{}{} 201 } 202 } 203 204 // Release releases a token obtained by Acquire. 205 func (t *Throttle) Release() { 206 if t.c != nil { 207 <-t.c 208 } 209 t.wg.Done() 210 } 211 212 // Wait blocks goroutine until the number of calls to Release matches the number of 213 // calls to Acquire. 214 func (t *Throttle) Wait() { 215 t.wg.Wait() 216 }