github.com/cornelk/go-cloud@v0.17.1/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 for _, a := range actions { 65 if a.Kind == Get { 66 // If there was a prior write with this key, make sure this get 67 // happens after the writes. 68 if _, ok := writes[a.Key]; ok { 69 agets[a.Key] = a 70 } else { 71 cgets[a.Key] = a 72 } 73 } else { 74 // This is a write. A prior get on the same key was put into cgets; move 75 // it to bgets because it has to happen before writes. 76 if g, ok := cgets[a.Key]; ok { 77 delete(cgets, a.Key) 78 bgets[a.Key] = g 79 } 80 writes[a.Key] = a 81 } 82 } 83 84 vals := func(m map[interface{}]*Action) []*Action { 85 var as []*Action 86 for _, v := range m { 87 as = append(as, v) 88 } 89 // Sort so the order is always the same for replay. 90 sort.Slice(as, func(i, j int) bool { return as[i].Index < as[j].Index }) 91 return as 92 } 93 94 return vals(bgets), vals(cgets), vals(writes), vals(agets) 95 } 96 97 // AsFunc creates and returns an "as function" that behaves as follows: 98 // If its argument is a pointer to the same type as val, the argument is set to val 99 // and the function returns true. Otherwise, the function returns false. 100 func AsFunc(val interface{}) func(interface{}) bool { 101 rval := reflect.ValueOf(val) 102 wantType := reflect.PtrTo(rval.Type()) 103 return func(i interface{}) bool { 104 if i == nil { 105 return false 106 } 107 ri := reflect.ValueOf(i) 108 if ri.Type() != wantType { 109 return false 110 } 111 ri.Elem().Set(rval) 112 return true 113 } 114 } 115 116 // GroupByFieldPath collect the Get actions into groups with the same set of 117 // field paths. 118 func GroupByFieldPath(gets []*Action) [][]*Action { 119 // This is quadratic in the worst case, but it's unlikely that there would be 120 // many Gets with different field paths. 121 var groups [][]*Action 122 seen := map[*Action]bool{} 123 for len(seen) < len(gets) { 124 var g []*Action 125 for _, a := range gets { 126 if !seen[a] { 127 if len(g) == 0 || fpsEqual(g[0].FieldPaths, a.FieldPaths) { 128 g = append(g, a) 129 seen[a] = true 130 } 131 } 132 } 133 groups = append(groups, g) 134 } 135 return groups 136 } 137 138 // Report whether two lists of field paths are equal. 139 func fpsEqual(fps1, fps2 [][]string) bool { 140 // TODO?: We really care about sets of field paths, but that's too tedious to determine. 141 if len(fps1) != len(fps2) { 142 return false 143 } 144 for i, fp1 := range fps1 { 145 if !FieldPathsEqual(fp1, fps2[i]) { 146 return false 147 } 148 } 149 return true 150 } 151 152 // FieldPathsEqual reports whether two field paths are equal. 153 func FieldPathsEqual(fp1, fp2 []string) bool { 154 if len(fp1) != len(fp2) { 155 return false 156 } 157 for i, s1 := range fp1 { 158 if s1 != fp2[i] { 159 return false 160 } 161 } 162 return true 163 } 164 165 // FieldPathEqualsField reports whether a field path equals a field. 166 // This is a convenience for FieldPathsEqual(fp, []string{s}). 167 func FieldPathEqualsField(fp []string, s string) bool { 168 return len(fp) == 1 && fp[0] == s 169 } 170 171 // Throttle is used to limit the number of outstanding activities, like RPCs. 172 // It acts like a combination of a semaphore and a WaitGroup. 173 type Throttle struct { 174 c chan struct{} // token semaphore 175 wg sync.WaitGroup 176 } 177 178 // NewThrottle returns a Throttle that will allow max calls to Acquire that 179 // are not matched with Release calls before blocking. 180 // If max is <= 0, there is no throttling: Acquire always returns immediately. 181 func NewThrottle(max int) *Throttle { 182 t := &Throttle{} 183 if max > 0 { 184 t.c = make(chan struct{}, max) 185 } 186 return t 187 } 188 189 // Acquire blocks until a token is available, then acquires it and returns. 190 // Acquire is deliberately not sensitive to context.Context, because it assumes 191 // that whatever acquires a token will be context-sensitive, and thus will release 192 // the token when the context is done. 193 func (t *Throttle) Acquire() { 194 t.wg.Add(1) 195 if t.c != nil { 196 t.c <- struct{}{} 197 } 198 } 199 200 // Release releases a token obtained by Acquire. 201 func (t *Throttle) Release() { 202 if t.c != nil { 203 <-t.c 204 } 205 t.wg.Done() 206 } 207 208 // Wait blocks goroutine until the number of calls to Release matches the number of 209 // calls to Acquire. 210 func (t *Throttle) Wait() { 211 t.wg.Wait() 212 }