Commit 5fc83902 authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent 8ee4675a
......@@ -30,6 +30,7 @@ import (
"io"
"net/url"
"regexp"
"sync"
"lab.nexedi.com/kirr/go123/mem"
"lab.nexedi.com/kirr/go123/xerr"
......@@ -54,6 +55,10 @@ type Storage struct {
watchWG *xsync.WorkGroup
watchCancel func()
downOnce sync.Once
down chan struct{} // ready when storage is down
downErr error // reason for shutdown
}
// baseMutatedError is reported when Storage.base is detected to change.
......@@ -78,6 +83,7 @@ func (e *baseError) Error() string {
func (e *baseError) Cause() error { return e.err }
func (e *baseError) Unwrap() error { return e.err }
// watcher detects base mutation and proxies δ events to user watchq.
// it runs as separate goroutine.
func (d *Storage) watcher(ctx context.Context) error {
......@@ -111,11 +117,11 @@ func (d *Storage) watcher(ctx context.Context) error {
edown = fmt.Errorf("base: unexpected event %T", event)
}
d.shutdown(edown)
ev := &zodb.EventError{edown} // XXX + context
if d.watchq != nil {
d.watchq <- ev
}
// XXX d.shutdown(edown)
return edown
// event on δ -> proxy to user
......@@ -132,6 +138,15 @@ func (d *Storage) watcher(ctx context.Context) error {
}
}
// shutdown marks Storage as no longer being operational due to reason.
func (d *Storage) shutdown(reason error) {
d.downOnce.Do(func() {
d.downErr = reason
close(d.down)
})
}
var errClosed = errors.New("storage is closed")
// Close implements zodb.IStorageDriver .
func (d *Storage) Close() (err error) {
......@@ -141,6 +156,7 @@ func (d *Storage) Close() (err error) {
}
}()
d.shutdown(errClosed)
errδ := d.δ.Close()
errBase := d.base.Close()
......@@ -200,38 +216,51 @@ func (d *Storage) Load(ctx context.Context, xid zodb.Xid) (_ *mem.Buf, _ zodb.Ti
}
}()
data, serial, err := d.δ.Load(ctx, xid)
if err == nil {
// object data is present in δ
return data, serial, nil
if ready(d.down) {
return nil, zodb.InvalidTid, d.downErr
}
useBase := false
inδ := false
var eNoData *zodb.NoDataError
var eNoObject *zodb.NoObjectError
switch {
case errors.As(err, &eNoData):
if eNoData.DeletedAt != 0 {
// object deleted in δ -> whiteout
return data, serial, eNoData
} else {
// object present in δ but not yet created as of xid.at
inδ := false
if xid.At > d.baseAt0 {
data, serial, err := d.δ.Load(ctx, xid)
if err == nil {
// object data is present in δ
return data, serial, nil
}
useBase := false
switch {
case errors.As(err, &eNoData):
if eNoData.DeletedAt != 0 {
// object deleted in δ -> whiteout
return data, serial, eNoData
} else {
// object present in δ but not yet created as of xid.at
useBase = true
inδ = true
}
case errors.As(err, &eNoObject):
// object not created in δ
useBase = true
inδ = true
}
case errors.As(err, &eNoObject):
// object not created in δ
useBase = true
if !useBase {
return data, serial, err
}
}
if !useBase {
return data, serial, err
// cap .at in xid to .baseAt0 (we convert it back on error return, and
// it makes more robust wrt simultaneous base mutation).
xidBase := xid
if xid.At > d.baseAt0 {
xidBase.At = d.baseAt0
}
// XXX cap .at in xid to .baseAt0 ? (and convert back on error return)
data, serial, err = d.base.Load(ctx, xid)
data, serial, err := d.base.Load(ctx, xidBase)
if err == nil {
return data, serial, nil
}
......@@ -350,6 +379,8 @@ func openByURL(ctx context.Context, u *url.URL, opt *zodb.DriverOptions) (_ zodb
baseWatchq: baseWatchq,
δWatchq: δWatchq,
watchq: opt.Watchq,
down: make(chan struct{}),
}
// spawn watcher to listen on baseWatchq and shutdown storage if base changes.
......@@ -364,3 +395,16 @@ func openByURL(ctx context.Context, u *url.URL, opt *zodb.DriverOptions) (_ zodb
func init() {
zodb.RegisterDriver("demo", openByURL)
}
// misc
// ready returns whether c is ready.
func ready(c <-chan struct{}) bool {
select {
case <-c:
return true
default:
return false
}
}
......@@ -240,7 +240,7 @@ func TestWatchLoad_vs_BaseMutate(t *testing.T) {
t.Fatalf("after base mutate: load: unexpected error:\nhave: %s\nwant: %s",
err, errOk)
}
if !(data == nil && serial == 0) {
if !(data == nil && serial == zodb.InvalidTid) {
t.Fatalf("after base mutate: load: unexpected data=%v serial=%v", data, serial)
}
})
......
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