github.com/unigraph-dev/dgraph@v1.1.1-0.20200923154953-8b52b426f765/dgraph/cmd/live/batch.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 live 18 19 import ( 20 "context" 21 "fmt" 22 "math/rand" 23 "strings" 24 "sync" 25 "sync/atomic" 26 "time" 27 28 "google.golang.org/grpc" 29 "google.golang.org/grpc/codes" 30 "google.golang.org/grpc/status" 31 32 "github.com/dgraph-io/badger" 33 "github.com/dgraph-io/dgo" 34 "github.com/dgraph-io/dgo/protos/api" 35 "github.com/dgraph-io/dgo/y" 36 "github.com/dgraph-io/dgraph/x" 37 "github.com/dgraph-io/dgraph/xidmap" 38 "github.com/dustin/go-humanize/english" 39 ) 40 41 // batchMutationOptions sets the clients batch mode to Pending number of buffers each of Size. 42 // Running counters of number of rdfs processed, total time and mutations per second are printed 43 // if PrintCounters is set true. See Counter. 44 type batchMutationOptions struct { 45 Size int 46 Pending int 47 PrintCounters bool 48 MaxRetries uint32 49 // User could pass a context so that we can stop retrying requests once context is done 50 Ctx context.Context 51 } 52 53 // loader is the data structure held by the user program for all interactions with the Dgraph 54 // server. After making grpc connection a new Dgraph is created by function NewDgraphClient. 55 type loader struct { 56 opts batchMutationOptions 57 58 dc *dgo.Dgraph 59 alloc *xidmap.XidMap 60 ticker *time.Ticker 61 db *badger.DB 62 requestsWg sync.WaitGroup 63 // If we retry a request, we add one to retryRequestsWg. 64 retryRequestsWg sync.WaitGroup 65 66 // Miscellaneous information to print counters. 67 // Num of N-Quads sent 68 nquads uint64 69 // Num of txns sent 70 txns uint64 71 // Num of aborts 72 aborts uint64 73 // To get time elapsed 74 start time.Time 75 76 reqNum uint64 77 reqs chan api.Mutation 78 zeroconn *grpc.ClientConn 79 } 80 81 // Counter keeps a track of various parameters about a batch mutation. Running totals are printed 82 // if BatchMutationOptions PrintCounters is set to true. 83 type Counter struct { 84 // Number of N-Quads processed by server. 85 Nquads uint64 86 // Number of mutations processed by the server. 87 TxnsDone uint64 88 // Number of Aborts 89 Aborts uint64 90 // Time elapsed since the batch started. 91 Elapsed time.Duration 92 } 93 94 // handleError inspects errors and terminates if the errors are non-recoverable. 95 // A gRPC code is Internal if there is an unforeseen issue that needs attention. 96 // A gRPC code is Unavailable when we can't possibly reach the remote server, most likely the 97 // server expects TLS and our certificate does not match or the host name is not verified. When 98 // the node certificate is created the name much match the request host name. e.g., localhost not 99 // 127.0.0.1. 100 func handleError(err error, reqNum uint64, isRetry bool) { 101 s := status.Convert(err) 102 switch { 103 case s.Code() == codes.Internal, s.Code() == codes.Unavailable: 104 x.Fatalf(s.Message()) 105 case strings.Contains(s.Message(), "x509"): 106 x.Fatalf(s.Message()) 107 case s.Code() == codes.Aborted: 108 if !isRetry && opt.verbose { 109 fmt.Printf("Transaction #%d aborted. Will retry in background.\n", reqNum) 110 } 111 case strings.Contains(s.Message(), "Server overloaded."): 112 dur := time.Duration(1+rand.Intn(10)) * time.Minute 113 fmt.Printf("Server is overloaded. Will retry after %s.\n", dur.Round(time.Minute)) 114 time.Sleep(dur) 115 case err != y.ErrConflict && err != y.ErrAborted: 116 fmt.Printf("Error while mutating: %v s.Code %v\n", s.Message(), s.Code()) 117 } 118 } 119 120 func (l *loader) infinitelyRetry(req api.Mutation, reqNum uint64) { 121 defer l.retryRequestsWg.Done() 122 nretries := 1 123 for i := time.Millisecond; ; i *= 2 { 124 txn := l.dc.NewTxn() 125 req.CommitNow = true 126 _, err := txn.Mutate(l.opts.Ctx, &req) 127 if err == nil { 128 if opt.verbose { 129 fmt.Printf("Transaction #%d succeeded after %s.\n", 130 reqNum, english.Plural(nretries, "retry", "retries")) 131 } 132 atomic.AddUint64(&l.nquads, uint64(len(req.Set))) 133 atomic.AddUint64(&l.txns, 1) 134 return 135 } 136 nretries++ 137 handleError(err, reqNum, true) 138 atomic.AddUint64(&l.aborts, 1) 139 if i >= 10*time.Second { 140 i = 10 * time.Second 141 } 142 time.Sleep(i) 143 } 144 } 145 146 func (l *loader) request(req api.Mutation, reqNum uint64) { 147 txn := l.dc.NewTxn() 148 req.CommitNow = true 149 _, err := txn.Mutate(l.opts.Ctx, &req) 150 151 if err == nil { 152 atomic.AddUint64(&l.nquads, uint64(len(req.Set))) 153 atomic.AddUint64(&l.txns, 1) 154 return 155 } 156 handleError(err, reqNum, false) 157 atomic.AddUint64(&l.aborts, 1) 158 l.retryRequestsWg.Add(1) 159 go l.infinitelyRetry(req, reqNum) 160 } 161 162 // makeRequests can receive requests from batchNquads or directly from BatchSetWithMark. 163 // It doesn't need to batch the requests anymore. Batching is already done for it by the 164 // caller functions. 165 func (l *loader) makeRequests() { 166 defer l.requestsWg.Done() 167 for req := range l.reqs { 168 reqNum := atomic.AddUint64(&l.reqNum, 1) 169 l.request(req, reqNum) 170 } 171 } 172 173 func (l *loader) printCounters() { 174 period := 5 * time.Second 175 l.ticker = time.NewTicker(period) 176 start := time.Now() 177 178 var last Counter 179 for range l.ticker.C { 180 counter := l.Counter() 181 rate := float64(counter.Nquads-last.Nquads) / period.Seconds() 182 elapsed := time.Since(start).Round(time.Second) 183 timestamp := time.Now().Format("15:04:05Z0700") 184 fmt.Printf("[%s] Elapsed: %s Txns: %d N-Quads: %d N-Quads/s [last 5s]: %5.0f Aborts: %d\n", 185 timestamp, x.FixedDuration(elapsed), counter.TxnsDone, counter.Nquads, rate, counter.Aborts) 186 last = counter 187 } 188 } 189 190 // Counter returns the current state of the BatchMutation. 191 func (l *loader) Counter() Counter { 192 return Counter{ 193 Nquads: atomic.LoadUint64(&l.nquads), 194 TxnsDone: atomic.LoadUint64(&l.txns), 195 Elapsed: time.Since(l.start), 196 Aborts: atomic.LoadUint64(&l.aborts), 197 } 198 }