Permalink
Browse files

Use DB snapshot IDs instead of object versions. Real MVCC for transac…

…tions
  • Loading branch information...
1 parent 556458e commit 7c57bd9dc980ad637524fbb049fddcf46a62bbc5 @brendonh committed Feb 10, 2013
@@ -1,6 +1,8 @@
package loge
-import "testing"
+import (
+ "testing"
+)
type TestObj struct {
Name string
@@ -13,7 +15,6 @@ func TestSimpleCreation(test *testing.T) {
db.Transact(func(t *Transaction) {
t.Set("test", "one", &TestObj{Name: "One"})
-
var one = t.Read("test", "one").(*TestObj)
if one.Name != "One" {
test.Error("Created object missing in transaction")
@@ -49,8 +50,8 @@ func TestCreationScoping(test *testing.T) {
test.Error("Created object became visible after commit")
}
- if !trans3.Exists("test", "one") {
- test.Error("Created object not visible when first read after commit")
+ if trans3.Exists("test", "one") {
+ test.Error("Created object visible when first read after commit")
}
}
View
@@ -3,12 +3,14 @@ package loge
import (
"fmt"
"time"
+ "sync/atomic"
)
type LogeDB struct {
types typeMap
store LogeStore
cache objCache
+ lastSnapshotID uint64
lock spinLock
}
@@ -17,6 +19,7 @@ func NewLogeDB(store LogeStore) *LogeDB {
types: make(typeMap),
store: store,
cache: make(objCache),
+ lastSnapshotID: 1,
}
}
@@ -89,7 +92,12 @@ func (db *LogeDB) CreateType(name string, version uint16, exemplar interface{},
func (db *LogeDB) CreateTransaction() *Transaction {
- return NewTransaction(db)
+ var tID = db.lastSnapshotID
+ return NewTransaction(db, tID)
+}
+
+func (db *LogeDB) newSnapshotID() uint64 {
+ return atomic.AddUint64(&db.lastSnapshotID, 1)
}
func (db *LogeDB) Transact(actor Transactor, timeout time.Duration) bool {
@@ -130,15 +138,15 @@ func (db *LogeDB) FlushCache() int {
}
// -----------------------------------------------
-// Dirty Operations
+// One-shot Operations
// -----------------------------------------------
-func (db *LogeDB) DirtyExists(typeName string, key LogeKey) bool {
+func (db *LogeDB) ExistsOne(typeName string, key LogeKey) bool {
var obj = db.store.get(db.types[typeName], key)
return obj != nil
}
-func (db *LogeDB) DirtyRead(typeName string, key LogeKey) interface{} {
+func (db *LogeDB) ReadOne(typeName string, key LogeKey) interface{} {
var typ = db.types[typeName]
var obj = db.store.get(typ, key)
if obj == nil {
@@ -147,7 +155,7 @@ func (db *LogeDB) DirtyRead(typeName string, key LogeKey) interface{} {
return obj
}
-func (db *LogeDB) DirtyReadLinks(typeName string, linkName string, key LogeKey) []string {
+func (db *LogeDB) ReadLinksOne(typeName string, linkName string, key LogeKey) []string {
return db.store.getLinks(db.types[typeName], linkName, key)
}
@@ -184,7 +192,7 @@ func (db *LogeDB) ensureObj(ref objRef, load bool) *logeObject {
if !ok {
obj = initializeObject(db, typ, key)
- }
+ }
obj.Lock.SpinLock()
defer obj.Lock.Unlock()
@@ -205,7 +213,6 @@ func (db *LogeDB) ensureObj(ref objRef, load bool) *logeObject {
linkSet.Original = links
version = &objectVersion {
LogeObj: obj,
- Version: 0,
Object: linkSet,
}
obj.LinkName = ref.LinkName
@@ -223,7 +230,6 @@ func (db *LogeDB) ensureObj(ref objRef, load bool) *logeObject {
}
version = &objectVersion{
- Version: 0,
Object: object,
}
View
@@ -1,59 +0,0 @@
-package loge
-
-import (
- "testing"
- "reflect"
-)
-
-func TestDirtyOps(test *testing.T) {
- var db = NewLogeDB(NewMemStore())
- db.CreateType("test", 1, &TestObj{}, LinkSpec{ "other": "test" })
-
- db.Transact(func (t *Transaction) {
- t.Set("test", "foo", &TestObj{ "foo" })
- t.Set("test", "bar", &TestObj{ "bar" })
-
- t.AddLink("test", "other", "foo", "bar")
- t.AddLink("test", "other", "bar", "foo")
- }, 0)
-
- if !db.DirtyExists("test", "foo") {
- test.Error("Dirty foo doesn't exist")
- }
-
- if !db.DirtyExists("test", "bar") {
- test.Error("Dirty bar doesn't exist")
- }
-
- if db.DirtyExists("test", "wib") {
- test.Error("Dirty wib exists")
- }
-
- if db.DirtyRead("test", "foo").(*TestObj).Name != "foo" {
- test.Error("Dirty read has wrong name")
- }
-
- if db.DirtyRead("test", "bar").(*TestObj).Name != "bar" {
- test.Error("Dirty read has wrong name")
- }
-
- var wib = db.DirtyRead("test", "wib").(*TestObj)
- if wib != nil {
- test.Errorf("Missing obj is not nil (%v)", wib)
- }
-
- var fooLinks = db.DirtyReadLinks("test", "other", "foo")
- if !reflect.DeepEqual(fooLinks, []string{ "bar" }) {
- test.Errorf("Wrong dirty links: %v", fooLinks)
- }
-
- var barLinks = db.DirtyReadLinks("test", "other", "bar")
- if !reflect.DeepEqual(barLinks, []string{ "foo" }) {
- test.Errorf("Wrong dirty links: %v", barLinks)
- }
-
- var wibLinks = db.DirtyReadLinks("test", "other", "wib")
- if !reflect.DeepEqual(wibLinks, []string{}) {
- test.Errorf("Wrong dirty links: %v", wibLinks)
- }
-}
View
@@ -17,9 +17,10 @@ type logeObject struct {
type objectVersion struct {
LogeObj *logeObject
- Version int
Object interface{}
Dirty bool
+ snapshotID uint64
+ Previous *objectVersion
}
@@ -34,22 +35,35 @@ func initializeObject(db *LogeDB, t *logeType, key LogeKey) *logeObject {
}
}
+func (obj *logeObject) getTransactionVersion(sID uint64) *objectVersion {
+ var version = obj.Current
+ for version.snapshotID > sID {
+ version = version.Previous
+ if version == nil {
+ panic("Couldn't find version")
+ }
+ }
+ return version
+}
-func (obj *logeObject) newVersion() *objectVersion {
- var current = obj.Current
+func (obj *logeObject) newVersion(sID uint64) *objectVersion {
+ var current = obj.getTransactionVersion(sID)
var newObj = obj.Type.Copy(current.Object)
return &objectVersion{
LogeObj: obj,
- Version: current.Version + 1,
Object: newObj,
Dirty: true,
+ Previous: obj.Current,
+ snapshotID: current.snapshotID,
}
}
-func (obj *logeObject) applyVersion(version *objectVersion, context storeContext) {
+func (obj *logeObject) applyVersion(version *objectVersion, context storeContext, sID uint64) {
+ version.snapshotID = sID
obj.Current = version
+ obj.Loaded = true
if obj.LinkName == "" {
context.store(obj)
View
@@ -0,0 +1,59 @@
+package loge
+
+import (
+ "testing"
+ "reflect"
+)
+
+func TestOneshotOps(test *testing.T) {
+ var db = NewLogeDB(NewMemStore())
+ db.CreateType("test", 1, &TestObj{}, LinkSpec{ "other": "test" })
+
+ db.Transact(func (t *Transaction) {
+ t.Set("test", "foo", &TestObj{ "foo" })
+ t.Set("test", "bar", &TestObj{ "bar" })
+
+ t.AddLink("test", "other", "foo", "bar")
+ t.AddLink("test", "other", "bar", "foo")
+ }, 0)
+
+ if !db.ExistsOne("test", "foo") {
+ test.Error("One-shot foo doesn't exist")
+ }
+
+ if !db.ExistsOne("test", "bar") {
+ test.Error("One-shot bar doesn't exist")
+ }
+
+ if db.ExistsOne("test", "wib") {
+ test.Error("One-shot wib exists")
+ }
+
+ if db.ReadOne("test", "foo").(*TestObj).Name != "foo" {
+ test.Error("One-shot read has wrong name")
+ }
+
+ if db.ReadOne("test", "bar").(*TestObj).Name != "bar" {
+ test.Error("One1shot read has wrong name")
+ }
+
+ var wib = db.ReadOne("test", "wib").(*TestObj)
+ if wib != nil {
+ test.Errorf("Missing obj is not nil (%v)", wib)
+ }
+
+ var fooLinks = db.ReadLinksOne("test", "other", "foo")
+ if !reflect.DeepEqual(fooLinks, []string{ "bar" }) {
+ test.Errorf("Wrong one-shot links: %v", fooLinks)
+ }
+
+ var barLinks = db.ReadLinksOne("test", "other", "bar")
+ if !reflect.DeepEqual(barLinks, []string{ "foo" }) {
+ test.Errorf("Wrong one-shot links: %v", barLinks)
+ }
+
+ var wibLinks = db.ReadLinksOne("test", "other", "wib")
+ if !reflect.DeepEqual(wibLinks, []string{}) {
+ test.Errorf("Wrong one-shot links: %v", wibLinks)
+ }
+}
View
@@ -22,14 +22,16 @@ type Transaction struct {
context transactionContext
versions map[string]*objectVersion
state TransactionState
+ snapshotID uint64
}
-func NewTransaction(db *LogeDB) *Transaction {
+func NewTransaction(db *LogeDB, sID uint64) *Transaction {
return &Transaction{
db: db,
context: db.store.newContext(),
versions: make(map[string]*objectVersion),
state: ACTIVE,
+ snapshotID: sID,
}
}
@@ -125,7 +127,7 @@ func (t *Transaction) getObj(ref objRef, forWrite bool, load bool) *objectVersio
if ok {
if forWrite {
if !version.Dirty {
- version = version.LogeObj.newVersion()
+ version = version.LogeObj.newVersion(t.snapshotID)
t.versions[objKey] = version
}
}
@@ -140,9 +142,9 @@ func (t *Transaction) getObj(ref objRef, forWrite bool, load bool) *objectVersio
logeObj.RefCount++
if forWrite {
- version = logeObj.newVersion()
+ version = logeObj.newVersion(t.snapshotID)
} else {
- version = logeObj.Current
+ version = logeObj.getTransactionVersion(t.snapshotID)
}
t.versions[objKey] = version
@@ -183,26 +185,20 @@ func (t *Transaction) tryCommit() bool {
}
defer obj.Lock.Unlock()
- var expectedVersion int
- if version.Dirty {
- expectedVersion = obj.Current.Version + 1
- } else {
- expectedVersion = obj.Current.Version
- }
-
- if version.Version != expectedVersion {
+ if obj.Current.snapshotID > t.snapshotID {
t.state = ABORTED
return true
}
}
var context = t.context
+ var sID = t.db.newSnapshotID()
for _, version := range t.versions {
- version.LogeObj.RefCount--
if version.Dirty {
- version.LogeObj.applyVersion(version, context)
+ version.LogeObj.applyVersion(version, context, sID)
}
+ version.LogeObj.RefCount--
}
var err = context.commit()
Oops, something went wrong.

0 comments on commit 7c57bd9

Please sign in to comment.