Example #1
0
func (s *suite) TestSetStateServers(c *gc.C) {
	changes, err := s.State.EnsureAvailability(5, constraints.Value{}, "precise")
	c.Assert(err, gc.IsNil)
	c.Assert(changes.Added, gc.HasLen, 5)

	session := s.State.MongoSession()
	db := session.DB("juju")
	runner := txn.NewRunner(db.C("txns"))
	err = setStateServers0([]string{"0", "1", "4"}, s.State, db, runner)
	c.Assert(err, gc.IsNil)
}
Example #2
0
func (s *S) SetUpTest(c *C) {
	txn.SetChaos(txn.Chaos{})
	txn.SetLogger(c)
	txn.SetDebug(true)
	s.MgoSuite.SetUpTest(c)

	s.db = s.session.DB("test")
	s.tc = s.db.C("tc")
	s.sc = s.db.C("tc.stash")
	s.accounts = s.db.C("accounts")
	s.runner = txn.NewRunner(s.tc)
}
Example #3
0
func openState() (*state.State, *mgo.Database, *txn.Runner, error) {
	stInfo, err := stateInfo()
	if err != nil {
		return nil, nil, nil, fmt.Errorf("cannot get state info: %v", err)
	}
	st, err := state.Open(stInfo, mongo.DialOpts{
		Timeout: 20 * time.Second,
	}, nil)
	if err != nil {
		return nil, nil, nil, fmt.Errorf("cannot open state: %v", err)
	}
	db := st.MongoSession().DB("juju")
	runner := txn.NewRunner(db.C("txns"))
	return st, db, runner, nil
}
Example #4
0
func main() {
	log.SetFlags(log.Llongfile)
	session, err := mgo.Dial("localhost")
	if err != nil {
		panic(err)
	}
	defer session.Close()

	// Optional. Switch the session to a monotonic behavior.
	session.SetMode(mgo.Monotonic, true)

	tc := session.DB("txn_test").C("tc")

	runner := txn.NewRunner(tc)
	accounts := session.DB("txn_test").C("accounts")

	if err := accounts.Insert(bson.M{"_id": 0, "balance": 300}); err != nil {
		log.Fatalln(err)
	}
	if err := accounts.Insert(bson.M{"_id": 1, "balance": 100}); err != nil {
		log.Fatalln(err)
	}
	if err := accounts.Insert(bson.M{"_id": 2, "balance": 200}); err != nil {
		log.Fatalln(err)
	}
	if err := accounts.Insert(bson.M{"_id": 3, "balance": 400}); err != nil {
		log.Fatalln(err)
	}

	ops := []txn.Op{
		{
			C:      "accounts",
			Id:     0,
			Assert: bson.D{{"$or", []bson.D{{{"balance", 100}}, {{"balance", 300}}}}},
			Update: bson.D{{"$inc", bson.D{{"balance", 100}}}},
		},
		{
			C:      "accounts",
			Id:     1,
			Assert: bson.D{{"balance", 100}},
			Update: bson.D{{"$inc", bson.D{{"balance", 150}}}},
		},
	}

	if err := runner.Run(ops, "", nil); err != nil {
		log.Fatalln(err)
	}
}
Example #5
0
func (s *watcherSuite) SetUpTest(c *gc.C) {
	s.BaseSuite.SetUpTest(c)
	s.MgoSuite.SetUpTest(c)

	db := s.MgoSuite.Session.DB("juju")
	s.log = db.C("txnlog")
	s.log.Create(&mgo.CollectionInfo{
		Capped:   true,
		MaxBytes: 1000000,
	})
	s.stash = db.C("txn.stash")
	s.runner = txn.NewRunner(db.C("txn"))
	s.runner.ChangeLog(s.log)
	s.w = watcher.New(s.log)
	s.ch = make(chan watcher.Change)
}
Example #6
0
File: reg.go Project: ycl2045/verdb
// FIXME: add clean for txn data
func (self *Registry) txnRunner(sess *mgo.Session) *txn.Runner {
	// bson.M{"_id": bson.M{"$lt": bson.NewObjectIdWithTime(minus24h)}}
	repo := sess.DB(self.DbName).C("txn_collection")
	// remove txn older then 2 * 60 mins
	if self.lastCleanTxn.IsZero() {
		self.lastCleanTxn = time.Now()
	}
	if time.Now().Sub(self.lastCleanTxn) > 30*time.Minute {
		repo.RemoveAll(bson.M{
			"_id": bson.M{
				"$lt": bson.NewObjectIdWithTime(time.Now().Add(-60 * 2 * time.Minute)),
			},
		})
		self.lastCleanTxn = time.Now()
	}

	return txn.NewRunner(repo)
}
Example #7
0
func oneByOneClientTrans(session *mgo.Session) error {
	db := session.DB("test")
	txnCollection := db.C("transactions")
	var retries int = 0
	for i := 0; i < *Count; i++ {
		val := *Start + i
		// Reset retries per document
		for retries = 0; retries < *MaxRetry; retries++ {
			if err := begin(db); err != nil {
				if shouldRetry(err, session) {
					RetryCount += 1
					continue
				}
				return err
			}
			runner := txn.NewRunner(txnCollection)
			op := oneDocOp(val)
			txnId := bson.NewObjectId()
			if err := runner.Run([]txn.Op{op}, txnId, nil); err != nil {
				fmt.Printf("failed to insert %d\n%s\n", val, err)
				// ignore an error from rollback?
				if err2 := rollback(db); err2 != nil {
					// For now, don't print anything, because we're already in an error state.
					// fmt.Printf("failed to rollback while failing: %s\n", err2)
				}
				if shouldRetry(err, session) {
					RetryCount += 1
					continue
				}
				return err
			}
			if err := commit(db); err != nil {
				if shouldRetry(err, session) {
					RetryCount += 1
					continue
				}
				return err
			} else {
				break
			}
		}
	}
	return nil
}
Example #8
0
func (s *S) TestTxnQueueStashStressTest(c *C) {
	txn.SetChaos(txn.Chaos{
		SlowdownChance: 0.3,
		Slowdown:       50 * time.Millisecond,
	})
	defer txn.SetChaos(txn.Chaos{})

	// So we can run more iterations of the test in less time.
	txn.SetDebug(false)

	const runners = 10
	const inserts = 10
	const repeat = 100

	for r := 0; r < repeat; r++ {
		var wg sync.WaitGroup
		wg.Add(runners)
		for i := 0; i < runners; i++ {
			go func(i, r int) {
				defer wg.Done()

				session := s.session.New()
				defer session.Close()
				runner := txn.NewRunner(s.tc.With(session))

				for j := 0; j < inserts; j++ {
					ops := []txn.Op{{
						C:  "accounts",
						Id: fmt.Sprintf("insert-%d-%d", r, j),
						Insert: bson.M{
							"added-by": i,
						},
					}}
					err := runner.Run(ops, "", nil)
					if err != txn.ErrAborted {
						c.Check(err, IsNil)
					}
				}
			}(i, r)
		}
		wg.Wait()
	}
}
Example #9
0
func oneByOneClientTrans(db *mgo.Database) error {
	txnCollection := db.C("transactions")
	var retries int = 0
	if err := maybeBegin(db, *OneTrans, &retries); err != nil {
		return err
	}
	for i := 0; i < *Count; i++ {
		val := *Start + i
		// Reset retries per document
		for retries = 0; retries < *MaxRetry; retries++ {
			if err := maybeBegin(db, *EachTrans, &retries); err != nil {
				return err
			}
			runner := txn.NewRunner(txnCollection)
			op := oneDocOp(val)
			txnId := bson.NewObjectId()
			if err := runner.Run([]txn.Op{op}, txnId, nil); err != nil {
				fmt.Printf("could not insert %d\n%s\n", val, err)
				// ignore the error because we are returning anyway
				maybeRollback(db, *OneTrans || *EachTrans)
				if shouldRetry(err) {
					RetryCount += 1
					continue
				}
				return err
			}
			if err := maybeCommit(db, *EachTrans); err != nil {
				if shouldRetry(err) {
					RetryCount += 1
					continue
				}
				return err
			} else {
				break
			}
		}
	}
	// ignore the error because we are returning anyway
	return maybeCommit(db, *OneTrans)
}
Example #10
0
func bulkClientTrans(db *mgo.Database) error {
	txnCollection := db.C("transactions")
	var retries int = 0
	ops := make([]txn.Op, 0, *Count)
	for i := 0; i < *Count; i++ {
		ops = append(ops, oneDocOp(*Start+i))
	}
	for ; retries < *MaxRetry; retries++ {
		if err := maybeBegin(db, *OneTrans || *EachTrans, &retries); err != nil {
			return err
		}
		runner := txn.NewRunner(txnCollection)
		txnId := bson.NewObjectId()
		if err := runner.Run(ops, txnId, nil); err != nil {
			fmt.Printf("could not insert all\n%s\n", err)
			// ignore the error because we are returning anyway
			maybeRollback(db, *OneTrans || *EachTrans)
			if shouldRetry(err) {
				RetryCount += 1
				continue
			}
			return err
		}
		// ignore the error because we are returning anyway
		if err := maybeCommit(db, *OneTrans || *EachTrans); err != nil {
			if shouldRetry(err) {
				RetryCount += 1
				continue
			}
			return err
		} else {
			break
		}
	}
	return nil
}
Example #11
0
func simulate(c *C, server *dbtest.DBServer, params params) {
	seed := *seed
	if seed == 0 {
		seed = time.Now().UnixNano()
	}
	rand.Seed(seed)
	c.Logf("Seed: %v", seed)

	txn.SetChaos(txn.Chaos{
		KillChance:     params.killChance,
		SlowdownChance: params.slowdownChance,
		Slowdown:       params.slowdown,
	})
	defer txn.SetChaos(txn.Chaos{})

	session := server.Session()
	defer session.Close()

	db := session.DB("test")
	tc := db.C("tc")

	runner := txn.NewRunner(tc)

	tclog := db.C("tc.log")
	if params.changelog {
		info := mgo.CollectionInfo{
			Capped:   true,
			MaxBytes: 1000000,
		}
		err := tclog.Create(&info)
		c.Assert(err, IsNil)
		runner.ChangeLog(tclog)
	}

	accounts := db.C("accounts")
	for i := 0; i < params.accounts; i++ {
		err := accounts.Insert(M{"_id": i, "balance": 300})
		c.Assert(err, IsNil)
	}
	var stop time.Time
	if params.changes <= 0 {
		stop = time.Now().Add(*duration)
	}

	max := params.accounts
	if params.reinsertCopy || params.reinsertZeroed {
		max = int(float64(params.accounts) * 1.5)
	}

	changes := make(chan balanceChange, 1024)

	//session.SetMode(mgo.Eventual, true)
	for i := 0; i < params.workers; i++ {
		go func() {
			n := 0
			for {
				if n > 0 && n == params.changes {
					break
				}
				if !stop.IsZero() && time.Now().After(stop) {
					break
				}

				change := balanceChange{
					id:     bson.NewObjectId(),
					origin: rand.Intn(max),
					target: rand.Intn(max),
					amount: 100,
				}

				var old Account
				var oldExists bool
				if params.reinsertCopy || params.reinsertZeroed {
					if err := accounts.FindId(change.origin).One(&old); err != mgo.ErrNotFound {
						c.Check(err, IsNil)
						change.amount = old.Balance
						oldExists = true
					}
				}

				var ops []txn.Op
				switch {
				case params.reinsertCopy && oldExists:
					ops = []txn.Op{{
						C:      "accounts",
						Id:     change.origin,
						Assert: M{"balance": change.amount},
						Remove: true,
					}, {
						C:      "accounts",
						Id:     change.target,
						Assert: txn.DocMissing,
						Insert: M{"balance": change.amount},
					}}
				case params.reinsertZeroed && oldExists:
					ops = []txn.Op{{
						C:      "accounts",
						Id:     change.target,
						Assert: txn.DocMissing,
						Insert: M{"balance": 0},
					}, {
						C:      "accounts",
						Id:     change.origin,
						Assert: M{"balance": change.amount},
						Remove: true,
					}, {
						C:      "accounts",
						Id:     change.target,
						Assert: txn.DocExists,
						Update: M{"$inc": M{"balance": change.amount}},
					}}
				case params.changeHalf:
					ops = []txn.Op{{
						C:      "accounts",
						Id:     change.origin,
						Assert: M{"balance": M{"$gte": change.amount}},
						Update: M{"$inc": M{"balance": -change.amount / 2}},
					}, {
						C:      "accounts",
						Id:     change.target,
						Assert: txn.DocExists,
						Update: M{"$inc": M{"balance": change.amount / 2}},
					}, {
						C:      "accounts",
						Id:     change.origin,
						Update: M{"$inc": M{"balance": -change.amount / 2}},
					}, {
						C:      "accounts",
						Id:     change.target,
						Update: M{"$inc": M{"balance": change.amount / 2}},
					}}
				default:
					ops = []txn.Op{{
						C:      "accounts",
						Id:     change.origin,
						Assert: M{"balance": M{"$gte": change.amount}},
						Update: M{"$inc": M{"balance": -change.amount}},
					}, {
						C:      "accounts",
						Id:     change.target,
						Assert: txn.DocExists,
						Update: M{"$inc": M{"balance": change.amount}},
					}}
				}

				err := runner.Run(ops, change.id, nil)
				if err != nil && err != txn.ErrAborted && err != txn.ErrChaos {
					c.Check(err, IsNil)
				}
				n++
				changes <- change
			}
			changes <- balanceChange{}
		}()
	}

	alive := params.workers
	changeLog := make([]balanceChange, 0, 1024)
	for alive > 0 {
		change := <-changes
		if change.id == "" {
			alive--
		} else {
			changeLog = append(changeLog, change)
		}
	}
	c.Check(len(changeLog), Not(Equals), 0, Commentf("No operations were even attempted."))

	txn.SetChaos(txn.Chaos{})
	err := runner.ResumeAll()
	c.Assert(err, IsNil)

	n, err := accounts.Count()
	c.Check(err, IsNil)
	c.Check(n, Equals, params.accounts, Commentf("Number of accounts has changed."))

	n, err = accounts.Find(M{"balance": M{"$lt": 0}}).Count()
	c.Check(err, IsNil)
	c.Check(n, Equals, 0, Commentf("There are %d accounts with negative balance.", n))

	globalBalance := 0
	iter := accounts.Find(nil).Iter()
	account := Account{}
	for iter.Next(&account) {
		globalBalance += account.Balance
	}
	c.Check(iter.Close(), IsNil)
	c.Check(globalBalance, Equals, params.accounts*300, Commentf("Total amount of money should be constant."))

	// Compute and verify the exact final state of all accounts.
	balance := make(map[int]int)
	for i := 0; i < params.accounts; i++ {
		balance[i] += 300
	}
	var applied, aborted int
	for _, change := range changeLog {
		err := runner.Resume(change.id)
		if err == txn.ErrAborted {
			aborted++
			continue
		} else if err != nil {
			c.Fatalf("resuming %s failed: %v", change.id, err)
		}
		balance[change.origin] -= change.amount
		balance[change.target] += change.amount
		applied++
	}
	iter = accounts.Find(nil).Iter()
	for iter.Next(&account) {
		c.Assert(account.Balance, Equals, balance[account.Id])
	}
	c.Check(iter.Close(), IsNil)
	c.Logf("Total transactions: %d (%d applied, %d aborted)", len(changeLog), applied, aborted)

	if params.changelog {
		n, err := tclog.Count()
		c.Assert(err, IsNil)
		// Check if the capped collection is full.
		dummy := make([]byte, 1024)
		tclog.Insert(M{"_id": bson.NewObjectId(), "dummy": dummy})
		m, err := tclog.Count()
		c.Assert(err, IsNil)
		if m == n+1 {
			// Wasn't full, so it must have seen it all.
			c.Assert(err, IsNil)
			c.Assert(n, Equals, applied)
		}
	}
}
Example #12
0
File: restore.go Project: bac/juju
// PurgeTxn purges missing transation from restoreInfoC collection.
// These can be caused because this collection is heavy use while backing
// up and mongo 3.2 does not like this.
func (info *RestoreInfo) PurgeTxn() error {
	restoreInfo, closer := info.st.getRawCollection(restoreInfoC)
	defer closer()
	r := txn.NewRunner(restoreInfo)
	return r.PurgeMissing(restoreInfoC)
}