func (w *Worker) doTxn(q *Query) (*Result, error) { if q.TXN >= LAST_TXN { debug.PrintStack() clog.Error("Unknown transaction number %v\n", q.TXN) } w.NStats[NTXN]++ if len(q.accessParts) > 1 { w.NStats[NCROSSTXN]++ } w.E.Reset(q) x, err := w.txns[q.TXN](q, w.E) if err == EABORT { w.NStats[NABORTS]++ return nil, err } else if err == ENOKEY { w.NStats[NENOKEY]++ return nil, err } w.NStats[NREADKEYS] += int64(len(q.rKeys)) w.NStats[NWRITEKEYS] += int64(len(q.wKeys)) return x, err //return nil, nil }
func (w *Worker) ResetTID(bigger TID) { big := bigger >> 16 if big < w.next { clog.Error("%v How is supposedly bigger TID %v smaller than %v\n", w.ID, big, w.next) } w.next = TID(big + 1) }
func PrintStore(s *Store, nKeys int64, p Partitioner) { for i := int64(0); i < nKeys; i++ { k := Key(i) partNum := p.GetPartition(k) r := s.GetRecord(k, partNum) if r == nil { clog.Error("Error No Key") } clog.Info("Key %v: %v", k, r.Value()) } }
func getTxn(txntype string) (int, testbed.RecType) { if strings.Compare(txntype, "addone") == 0 { return testbed.ADD_ONE, testbed.SINGLEINT } else if strings.Compare(txntype, "updateint") == 0 { return testbed.RANDOM_UPDATE_INT, testbed.SINGLEINT } else if strings.Compare(txntype, "updatestring") == 0 { return testbed.RANDOM_UPDATE_STRING, testbed.STRINGLIST } else { clog.Error("Not Supported %s Transaction", txntype) return -1, -1 } }
func (zk *ZipfKey) GetOtherKey(pi int) Key { if !zk.isPartition { clog.Error("Should not be invoked for non-partition CC") } if zk.isZipf { rank := int64(zk.partZipf[pi].Uint64()) return zk.hp.GetKey(pi, int64(rank)) } else { return zk.hp.GetKey(pi, zk.partUniform[pi].Int63n(zk.pKeysArray[pi])) } }
func PrintPartition(s *Store, nKeys int64, p Partitioner, partNum int) { for i := int64(0); i < nKeys; i++ { k := Key(i) if p.GetPartition(k) != partNum { continue } r := s.GetRecord(k, partNum) if r == nil { clog.Error("Error No Key") } clog.Info("Key %v: %v", r.GetKey(), r.Value()) } }
func MakeRecord(k Key, v Value, rt RecType) Record { if *SysType == PARTITION { pr := &PRecord{ key: k, recType: rt, } // Initiate Value according to different types switch rt { case SINGLEINT: if v != nil { pr.intVal = v.(int64) } case STRINGLIST: if v != nil { var inputStrList = v.([]string) pr.stringVal = make([]string, len(inputStrList)) for i, _ := range inputStrList { pr.stringVal[i] = inputStrList[i] } } } return pr } else if *SysType == OCC { or := &ORecord{ key: k, recType: rt, last: wfmutex.WFMutex{}, } // Initiate Value according to different types switch rt { case SINGLEINT: if v != nil { or.intVal = v.(int64) } case STRINGLIST: if v != nil { var inputStrList = v.([]string) or.stringVal = make([]string, len(inputStrList)) for i, _ := range inputStrList { or.stringVal[i] = inputStrList[i] } } } return or } else { clog.Error("System Type %v Not Supported Yet", *SysType) return nil } }
func (or *ORecord) UpdateValue(val Value) bool { if val == nil { return false } switch or.recType { case SINGLEINT: or.intVal = *val.(*int64) case STRINGLIST: strAttr := val.(*StrAttr) if strAttr.index >= len(or.stringVal) { clog.Error("Index %v out of range array length %v", strAttr.index, len(or.stringVal)) } or.stringVal[strAttr.index] = strAttr.value } return true }
func NewWorker(id int, s *Store) *Worker { w := &Worker{ ID: id, store: s, txns: make([]TransactionFunc, LAST_TXN), NStats: make([]int64, LAST_STAT), } if *SysType == PARTITION { w.E = StartPTransaction(w) } else if *SysType == OCC { w.E = StartOTransaction(w) } else { clog.Error("OCC and 2PL not supported yet") } w.Register(ADD_ONE, AddOneTXN) w.Register(RANDOM_UPDATE_INT, UpdateIntTXN) w.Register(RANDOM_UPDATE_STRING, UpdateStringTXN) return w }
func (pr *PRecord) SetTID(tid TID) { clog.Error("Partition mode does not support SetTID Operation") }
func (dr *DRecord) GetTID() TID { clog.Error("Dummy Record does not support GetTID Operation") return 0 }
func (dr *DRecord) SetTID(tid TID) { clog.Error("Dummy Record does not support SetTID Operation") }
func (dr *DRecord) Unlock(tid TID) { clog.Error("Dummy Record does not support Unlock Operation") }
func (dr *DRecord) IsUnlocked() (bool, TID) { clog.Error("Dummy Record does not support IsUnlocked Operation") return false, 0 }
func (dr *DRecord) GetKey() Key { clog.Error("Dummy Record does not support GetKey Operation") return dr.key }
func (dr *DRecord) Lock() (bool, TID) { clog.Error("Dummy Record does not support Lock Operation") return false, 0 }
func (or *ORecord) SetTID(tid TID) { clog.Error("OCC mode does not support SetTID Operation") }
func (pr *PRecord) Lock() (bool, TID) { clog.Error("Partition mode does not support Lock Operation") return false, 0 }
func (pr *PRecord) Unlock(tid TID) { clog.Error("Partition mode does not support Unlock Operation") }
func (pr *PRecord) GetTID() TID { clog.Error("Partition mode does not support GetTID Operation") return 0 }
func (pr *PRecord) IsUnlocked() (bool, TID) { clog.Error("Partition mode does not support IsUnlocked Operation") return false, 0 }
func main() { flag.Parse() // set max cores used, number of clients and number of workers runtime.GOMAXPROCS(*testbed.NumPart) clients := *testbed.NumPart nworkers := *testbed.NumPart if *contention < 1 { clog.Error("Contention factor should be between no less than 1") } clog.Info("Number of clients %v, Number of workers %v \n", clients, nworkers) if *testbed.SysType == testbed.PARTITION { clog.Info("Using Partition-based CC\n") } else if *testbed.SysType == testbed.OCC { if *testbed.PhyPart { clog.Info("Using OCC with partition\n") } else { clog.Info("Using OCC\n") } } else { clog.Error("Not supported type %v CC\n", *testbed.SysType) } tt, dt := getTxn(*txntype) // create store s := testbed.NewStore() var nParts int var hp testbed.Partitioner = nil var pKeysArray []int64 var value interface{} if *testbed.SysType == testbed.PARTITION || *testbed.PhyPart { nParts = *testbed.NumPart pKeysArray = make([]int64, nParts) hp = &testbed.HashPartitioner{ NParts: int64(nParts), NKeys: int64(*nKeys), } var partNum int for i := int64(0); i < *nKeys; i++ { k := testbed.Key(i) partNum = hp.GetPartition(k) pKeysArray[partNum]++ if dt == testbed.SINGLEINT { value = int64(0) } else if dt == testbed.STRINGLIST { value = testbed.GenStringList() } s.CreateKV(k, value, dt, partNum) } } else { nParts = 1 for i := int64(0); i < *nKeys; i++ { k := testbed.Key(i) if dt == testbed.SINGLEINT { value = int64(0) } else if dt == testbed.STRINGLIST { value = testbed.GenStringList() } s.CreateKV(k, value, dt, 0) } } generators := make([]*testbed.TxnGen, nworkers) for i := 0; i < nworkers; i++ { p := &testbed.HashPartitioner{ NParts: int64(nParts), NKeys: int64(*nKeys), } zk := testbed.NewZipfKey(i, *nKeys, nParts, pKeysArray, *contention, p) generators[i] = testbed.NewTxnGen(i, tt, *rr, *txnlen, *mp, zk) } coord := testbed.NewCoordinator(nworkers, s) clog.Info("Done with Initialization") var wg sync.WaitGroup for i := 0; i < clients; i++ { wg.Add(1) go func(n int) { //var txn int64 //var count int w := coord.Workers[n] end_time := time.Now().Add(time.Duration(*nsec) * time.Second) for { tm := time.Now() if !end_time.After(tm) { break } q := generators[n].GenOneQuery() //q.DoNothing() w.NGen += time.Since(tm) tm = time.Now() for j := 0; j < TRIAL; j++ { _, err := w.One(q) if err == nil { break } else if err == testbed.ENOKEY { clog.Error("No Key Error") } else if err != testbed.EABORT { clog.Error("Not Support Error Type %v", err) } } //_, err := w.One(q) w.NExecute += time.Since(tm) /*if err == testbed.ENOKEY { clog.Error("No Key Error") break }*/ //txn++ } //clog.Info("Worker %d issues %d transactions\n", n, txn) wg.Done() }(i) } wg.Wait() f, err := os.OpenFile(*out, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) if err != nil { clog.Error("Open File Error %s\n", err.Error()) } defer f.Close() coord.PrintStats(f) if *benchStat != "" { bs, err := os.OpenFile(*benchStat, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) if err != nil { clog.Error("Open File Error %s\n", err.Error()) } defer bs.Close() //bs.WriteString(fmt.Sprintf("%v\t%v\n", *testbed.CrossPercent, coord.NStats[testbed.NTXN]-coord.NStats[testbed.NABORTS])) bs.WriteString(fmt.Sprintf("%.f\n", float64(coord.NStats[testbed.NTXN]-coord.NStats[testbed.NABORTS])/coord.NExecute.Seconds())) } }
func (o *OTransaction) Commit() TID { // Phase 1: Lock all write keys //for _, wk := range o.wKeys { for i := 0; i < len(o.wKeys); i++ { wk := &o.wKeys[i] var former TID var ok bool if ok, former = wk.rec.Lock(); !ok { o.w.NStats[NLOCKABORTS]++ return o.Abort() } wk.locked = true if former > o.maxSeen { o.maxSeen = former } } tid := o.w.commitTID() if tid <= o.maxSeen { o.w.ResetTID(o.maxSeen) tid = o.w.commitTID() if tid < o.maxSeen { clog.Error("%v MaxSeen %v, reset TID but %v<%v", o.w.ID, o.maxSeen, tid, o.maxSeen) } } // Phase 2: Check conflicts //for k, rk := range o.rKeys { for i := 0; i < len(o.rKeys); i++ { k := o.rKeys[i].k rk := &o.rKeys[i] //verify whether TID has changed var ok1, ok2 bool var tmpTID TID ok1, tmpTID = rk.rec.IsUnlocked() if tmpTID != rk.last { o.w.NStats[NRCHANGEABORTS]++ return o.Abort() } // Check whether read key is not in wKeys //_, ok2 = o.wKeys[k] ok2 = false for j := 0; j < len(o.wKeys); j++ { wk := &o.wKeys[j] if wk.k == k { ok2 = true break } } if !ok1 && !ok2 { o.w.NStats[NRWABORTS]++ return o.Abort() } } // Phase 3: Apply all writes for i, _ := range o.wKeys { wk := &o.wKeys[i] //wk.rec.UpdateValue(wk.v) wk.rec.UpdateValue(&wk.intVal) wk.rec.Unlock(tid) } return tid }