github.com/unigraph-dev/dgraph@v1.1.1-0.20200923154953-8b52b426f765/contrib/integration/acctupsert/main.go (about) 1 /* 2 * Copyright 2017-2018 Dgraph Labs, Inc. and Contributors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "context" 21 "encoding/json" 22 "flag" 23 "fmt" 24 "math/rand" 25 "strings" 26 "sync" 27 "sync/atomic" 28 "time" 29 30 "github.com/dgraph-io/dgo" 31 "github.com/dgraph-io/dgo/protos/api" 32 "github.com/dgraph-io/dgo/x" 33 "github.com/dgraph-io/dgo/y" 34 "github.com/dgraph-io/dgraph/testutil" 35 ) 36 37 var ( 38 alpha = flag.String("alpha", "localhost:9180", "dgraph alpha address") 39 concurr = flag.Int("c", 3, "number of concurrent upserts per account") 40 ) 41 42 var ( 43 firsts = []string{"Paul", "Eric", "Jack", "John", "Martin"} 44 lasts = []string{"Brown", "Smith", "Robinson", "Waters", "Taylor"} 45 ages = []int{20, 25, 30, 35} 46 types = []string{"CEO", "COO", "CTO", "CFO"} 47 ) 48 49 type account struct { 50 first string 51 last string 52 age int 53 } 54 55 var accounts []account 56 57 func init() { 58 for _, first := range firsts { 59 for _, last := range lasts { 60 for _, age := range ages { 61 accounts = append(accounts, account{ 62 first: first, 63 last: last, 64 age: age, 65 }) 66 } 67 } 68 } 69 } 70 71 func main() { 72 flag.Parse() 73 c := testutil.DgraphClientWithGroot(*alpha) 74 setup(c) 75 fmt.Println("Doing upserts") 76 doUpserts(c) 77 fmt.Println("Checking integrity") 78 checkIntegrity(c) 79 } 80 81 func setup(c *dgo.Dgraph) { 82 ctx := context.Background() 83 x.Check(c.Alter(ctx, &api.Operation{ 84 DropAll: true, 85 })) 86 x.Check(c.Alter(ctx, &api.Operation{ 87 Schema: ` 88 first: string @index(term) @upsert . 89 last: string @index(hash) @upsert . 90 age: int @index(int) @upsert . 91 when: int . 92 `, 93 })) 94 } 95 96 func doUpserts(c *dgo.Dgraph) { 97 var wg sync.WaitGroup 98 wg.Add(len(accounts) * *concurr) 99 for _, acct := range accounts { 100 for i := 0; i < *concurr; i++ { 101 go func(acct account) { 102 upsert(c, acct) 103 wg.Done() 104 }(acct) 105 } 106 } 107 wg.Wait() 108 } 109 110 var ( 111 successCount uint64 112 retryCount uint64 113 lastStatus time.Time 114 ) 115 116 func upsert(c *dgo.Dgraph, acc account) { 117 for { 118 if time.Since(lastStatus) > 100*time.Millisecond { 119 fmt.Printf("[%s] Success: %d Retries: %d\n", time.Now().Format(time.Stamp), 120 atomic.LoadUint64(&successCount), atomic.LoadUint64(&retryCount)) 121 lastStatus = time.Now() 122 } 123 err := tryUpsert(c, acc) 124 if err == nil { 125 atomic.AddUint64(&successCount, 1) 126 return 127 } else if err == y.ErrAborted { 128 // pass 129 } else { 130 fmt.Printf("ERROR: %v", err) 131 } 132 atomic.AddUint64(&retryCount, 1) 133 } 134 } 135 136 func tryUpsert(c *dgo.Dgraph, acc account) error { 137 ctx := context.Background() 138 139 txn := c.NewTxn() 140 defer func() { _ = txn.Discard(ctx) }() 141 q := fmt.Sprintf(` 142 { 143 get(func: eq(first, %q)) @filter(eq(last, %q) AND eq(age, %d)) { 144 uid 145 expand(_all_) {uid} 146 } 147 } 148 `, acc.first, acc.last, acc.age) 149 resp, err := txn.Query(ctx, q) 150 x.Check(err) 151 152 decode := struct { 153 Get []struct { 154 Uid *string 155 } 156 }{} 157 x.Check(json.Unmarshal(resp.GetJson(), &decode)) 158 159 x.AssertTrue(len(decode.Get) <= 1) 160 t := rand.Intn(len(types)) 161 162 var uid string 163 if len(decode.Get) == 1 { 164 x.AssertTrue(decode.Get[0].Uid != nil) 165 uid = *decode.Get[0].Uid 166 } else { 167 nqs := fmt.Sprintf(` 168 _:acct <first> %q . 169 _:acct <last> %q . 170 _:acct <age> "%d"^^<xs:int> . 171 _:acct <%s> "" . 172 `, 173 acc.first, acc.last, acc.age, types[t], 174 ) 175 mu := &api.Mutation{SetNquads: []byte(nqs)} 176 assigned, err := txn.Mutate(ctx, mu) 177 if err != nil { 178 return err 179 } 180 uid = assigned.GetUids()["acct"] 181 x.AssertTrue(uid != "") 182 } 183 184 nq := fmt.Sprintf(` 185 <%s> <when> "%d"^^<xs:int> . 186 `, 187 uid, time.Now().Nanosecond(), 188 ) 189 mu := &api.Mutation{SetNquads: []byte(nq)} 190 if _, err = txn.Mutate(ctx, mu); err != nil { 191 return err 192 } 193 194 return txn.Commit(ctx) 195 } 196 197 func checkIntegrity(c *dgo.Dgraph) { 198 ctx := context.Background() 199 200 q := fmt.Sprintf(` 201 { 202 all(func: anyofterms(first, %q)) { 203 first 204 last 205 age 206 } 207 } 208 `, strings.Join(firsts, " ")) 209 resp, err := c.NewTxn().Query(ctx, q) 210 x.Check(err) 211 212 decode := struct { 213 All []struct { 214 First *string 215 Last *string 216 Age *int 217 } 218 }{} 219 x.Check(json.Unmarshal(resp.GetJson(), &decode)) 220 221 // Make sure there is exactly one of each account. 222 accountSet := make(map[string]struct{}) 223 for _, record := range decode.All { 224 x.AssertTrue(record.First != nil) 225 x.AssertTrue(record.Last != nil) 226 x.AssertTrue(record.Age != nil) 227 str := fmt.Sprintf("%s_%s_%d", *record.First, *record.Last, *record.Age) 228 accountSet[str] = struct{}{} 229 } 230 x.AssertTrue(len(accountSet) == len(accounts)) 231 for _, acct := range accounts { 232 str := fmt.Sprintf("%s_%s_%d", acct.first, acct.last, acct.age) 233 _, ok := accountSet[str] 234 x.AssertTrue(ok) 235 } 236 }