Commit 4672ebb0 authored by Kirill Smelkov's avatar Kirill Smelkov

X Access to Connection.cache

parent f7789b0e
...@@ -50,10 +50,24 @@ type Connection struct { ...@@ -50,10 +50,24 @@ type Connection struct {
txn transaction.Transaction // opened under this txn; nil if idle in DB pool. txn transaction.Transaction // opened under this txn; nil if idle in DB pool.
at Tid // current view of database; stable inside a transaction. at Tid // current view of database; stable inside a transaction.
// XXX document it is only a cache - i.e. this is only partial mapping, not for whole db cache LiveCache // cache of connection's in-RAM objects
// {} oid -> obj }
//
// rationale: // LiveCache keeps registry of live in-RAM objects for a Connection.
//
// It semantically consists of
//
// {} oid -> obj
//
// but does not hold strong reference to cached objects.
//
// LiveCache is safe for multiple simultaneous read access.
// LiveCache is not safe for multiple simultaneous read/write access -
// the caller must explicitly serialize access with e.g. .Lock() .
//
// XXX try to hide locking from user?
type LiveCache struct {
// rationale for using weakref:
// //
// on invalidations: we need to go oid -> obj and invalidate it. // on invalidations: we need to go oid -> obj and invalidate it.
// -> Connection need to keep {} oid -> obj. // -> Connection need to keep {} oid -> obj.
...@@ -99,7 +113,8 @@ type Connection struct { ...@@ -99,7 +113,8 @@ type Connection struct {
// //
// NOTE2 finalizers don't run on when they are attached to an object in cycle. // NOTE2 finalizers don't run on when they are attached to an object in cycle.
// Hopefully we don't have cycles with BTree/Bucket. // Hopefully we don't have cycles with BTree/Bucket.
objmu sync.Mutex
sync.Mutex
objtab map[Oid]*weak.Ref // oid -> weak.Ref(IPersistent) objtab map[Oid]*weak.Ref // oid -> weak.Ref(IPersistent)
// hooks for application to influence live caching decisions. // hooks for application to influence live caching decisions.
...@@ -127,7 +142,9 @@ func newConnection(db *DB, at Tid) *Connection { ...@@ -127,7 +142,9 @@ func newConnection(db *DB, at Tid) *Connection {
stor: db.stor, stor: db.stor,
db: db, db: db,
at: at, at: at,
cache: LiveCache{
objtab: make(map[Oid]*weak.Ref), objtab: make(map[Oid]*weak.Ref),
},
} }
} }
...@@ -137,6 +154,11 @@ func (conn *Connection) At() Tid { ...@@ -137,6 +154,11 @@ func (conn *Connection) At() Tid {
return conn.at return conn.at
} }
// Cache returns connection's cache of live objects.
func (conn *Connection) Cache() *LiveCache {
return &conn.cache
}
// wrongClassError is the error cause returned when ZODB object's class is not what was expected. // wrongClassError is the error cause returned when ZODB object's class is not what was expected.
type wrongClassError struct { type wrongClassError struct {
want, have string want, have string
...@@ -146,31 +168,39 @@ func (e *wrongClassError) Error() string { ...@@ -146,31 +168,39 @@ func (e *wrongClassError) Error() string {
return fmt.Sprintf("wrong class: want %q; have %q", e.want, e.have) return fmt.Sprintf("wrong class: want %q; have %q", e.want, e.have)
} }
// get is like Get, but used when we already know object class. // Get lookups object corresponding to oid in the cache.
// //
// Use-case: in ZODB references are (pyclass, oid), so new ghost is created // It is not safe to call Get from multiple goroutines simultaneously.
// without further loading anything. func (cache *LiveCache) Get(oid Oid) IPersistent {
func (conn *Connection) get(class string, oid Oid) (IPersistent, error) { wobj := cache.objtab[oid]
conn.objmu.Lock() // XXX -> rlock?
wobj := conn.objtab[oid]
var obj IPersistent var obj IPersistent
checkClass := false
if wobj != nil { if wobj != nil {
if xobj := wobj.Get(); xobj != nil { if xobj := wobj.Get(); xobj != nil {
obj = xobj.(IPersistent) obj = xobj.(IPersistent)
} }
} }
return obj
}
// get is like Get, but used when we already know object class.
//
// Use-case: in ZODB references are (pyclass, oid), so new ghost is created
// without further loading anything.
func (conn *Connection) get(class string, oid Oid) (IPersistent, error) {
conn.cache.Lock() // XXX -> rlock?
obj := conn.cache.Get(oid)
checkClass := false
if obj == nil { if obj == nil {
obj = newGhost(class, oid, conn) obj = newGhost(class, oid, conn)
//if obj == nil { //if obj == nil {
// conn.objmu.Unlock() // conn.cache.Unlock()
// return nil, fmt.Errorf("get %s: class %q not supported", Xid{conn.at, oid}, class) // return nil, fmt.Errorf("get %s: class %q not supported", Xid{conn.at, oid}, class)
//} //}
conn.objtab[oid] = weak.NewRef(obj) conn.cache.objtab[oid] = weak.NewRef(obj)
} else { } else {
checkClass = true checkClass = true
} }
conn.objmu.Unlock() conn.cache.Unlock()
if checkClass { if checkClass {
if cls := ClassOf(obj); class != cls { if cls := ClassOf(obj); class != cls {
...@@ -196,20 +226,16 @@ func (conn *Connection) Get(ctx context.Context, oid Oid) (_ IPersistent, err er ...@@ -196,20 +226,16 @@ func (conn *Connection) Get(ctx context.Context, oid Oid) (_ IPersistent, err er
conn.checkTxnCtx(ctx, "Get") conn.checkTxnCtx(ctx, "Get")
defer xerr.Contextf(&err, "Get %s", oid) defer xerr.Contextf(&err, "Get %s", oid)
conn.objmu.Lock() // XXX -> rlock? conn.cache.Lock() // XXX -> rlock?
wobj := conn.objtab[oid] obj := conn.cache.Get(oid)
var xobj interface{} conn.cache.Unlock()
if wobj != nil {
xobj = wobj.Get()
}
conn.objmu.Unlock()
// object was already there in objtab. // object was already there in cache.
if xobj != nil { if obj != nil {
return xobj.(IPersistent), nil return obj, nil
} }
// object is not there in objtab - raw load it, get its class -> get(pyclass, oid) // object is not in cache - raw load it, get its class -> get(pyclass, oid)
// XXX "py always" hardcoded // XXX "py always" hardcoded
class, pystate, serial, err := conn.loadpy(ctx, oid) class, pystate, serial, err := conn.loadpy(ctx, oid)
if err != nil { if err != nil {
...@@ -217,7 +243,7 @@ func (conn *Connection) Get(ctx context.Context, oid Oid) (_ IPersistent, err er ...@@ -217,7 +243,7 @@ func (conn *Connection) Get(ctx context.Context, oid Oid) (_ IPersistent, err er
return nil, err return nil, err
} }
obj, err := conn.get(class, oid) obj, err = conn.get(class, oid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -52,6 +52,7 @@ type DB struct { ...@@ -52,6 +52,7 @@ type DB struct {
// information about invalidations // information about invalidations
// XXX -> Storage. XXX or -> Cache? (so it is not duplicated many times for many DB case) // XXX -> Storage. XXX or -> Cache? (so it is not duplicated many times for many DB case)
// XXX -> ΔTail<tid, oid>
invTab []invEntry // order by ↑= .tid invTab []invEntry // order by ↑= .tid
} }
......
...@@ -224,7 +224,7 @@ func (obj *Persistent) PDeactivate() { ...@@ -224,7 +224,7 @@ func (obj *Persistent) PDeactivate() {
// no constant load/unload on object access. XXX -> MRU cache? // no constant load/unload on object access. XXX -> MRU cache?
// NOTE wcfs manages its objects explicitly and does not need this. // NOTE wcfs manages its objects explicitly and does not need this.
if cc := obj.jar.cacheControl; cc != nil { if cc := obj.jar.cache.cacheControl; cc != nil {
if !cc.WantEvict(obj.instance) { if !cc.WantEvict(obj.instance) {
return return
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment