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  }