Commit 737414e3 authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent c78a68ae
......@@ -56,7 +56,7 @@ type Cache struct {
sizeMax int // cache is allowed to occupy not more than this
}
// oidCacheEntry maintains cached revisions for 1 oid
// oidCacheEntry maintains cached revisions for 1 oid.
type oidCacheEntry struct {
oid Oid
......@@ -70,7 +70,7 @@ type oidCacheEntry struct {
rcev []*revCacheEntry
}
// revCacheEntry is information about 1 cached oid revision
// revCacheEntry is information about 1 cached oid revision.
type revCacheEntry struct {
parent *oidCacheEntry // oidCacheEntry holding us
inLRU lruHead // in Cache.lru; protected by Cache.gcMu
......@@ -409,7 +409,7 @@ func (c *Cache) loadRCE(ctx context.Context, rce *revCacheEntry) {
c.gcMu.Unlock()
}
// tryMerge tries to merge rce prev into next
// tryMerge tries to merge rce prev into next.
//
// both prev and next must be already loaded.
// prev and next must come adjacent to each other in parent.rcev with
......@@ -561,7 +561,7 @@ func (oce *oidCacheEntry) release() {
// ----------------------------------------
// isErrNoData returns whether an error is due to "there is no such data in
// database", not e.g. some IO loading error
// database", not e.g. some IO loading error.
func isErrNoData(err error) bool {
switch err.(type) {
default:
......@@ -631,7 +631,7 @@ func (oce *oidCacheEntry) del(rce *revCacheEntry) {
}
// loaded reports whether rce was already loaded
// loaded reports whether rce was already loaded.
//
// must be called with rce.parent locked.
func (rce *revCacheEntry) loaded() bool {
......@@ -653,7 +653,7 @@ func (h *lruHead) rceFromInLRU() (rce *revCacheEntry) {
return (*revCacheEntry)(urce)
}
// errDB returns error about database being inconsistent
// errDB returns error about database being inconsistent.
func errDB(oid Oid, format string, argv ...interface{}) error {
// XXX -> separate type?
return fmt.Errorf("cache: database inconsistency: oid: %v: "+format,
......
......@@ -35,13 +35,13 @@ import (
"github.com/kylelemons/godebug/pretty"
)
// tStorage implements read-only storage for cache testing
// tStorage implements read-only storage for cache testing.
type tStorage struct {
// oid -> [](.serial↑, .data)
dataMap map[Oid][]tOidData
}
// data for oid for 1 revision
// data for oid for 1 revision.
type tOidData struct {
serial Tid
data []byte
......
......@@ -29,19 +29,19 @@ import (
"lab.nexedi.com/kirr/go123/mem"
)
// OpenOptions describes options for OpenStorage
// OpenOptions describes options for OpenStorage.
type OpenOptions struct {
ReadOnly bool // whether to open storage as read-only
NoCache bool // don't use cache for read/write operations
}
// DriverOpener is a function to open a storage driver
// DriverOpener is a function to open a storage driver.
type DriverOpener func (ctx context.Context, u *url.URL, opt *OpenOptions) (IStorageDriver, error)
// {} scheme -> DriverOpener
var driverRegistry = map[string]DriverOpener{}
// RegisterDriver registers opener to be used for URLs with scheme
// RegisterDriver registers opener to be used for URLs with scheme.
func RegisterDriver(scheme string, opener DriverOpener) {
if _, already := driverRegistry[scheme]; already {
panic(fmt.Errorf("ZODB URL scheme %q was already registered", scheme))
......@@ -107,7 +107,7 @@ type storage struct {
}
// loading always goes through cache - this way prefetching can work
// loading goes through cache - this way prefetching can work
func (s *storage) Load(ctx context.Context, xid Xid) (*mem.Buf, Tid, error) {
// XXX here: offload xid validation from cache and driver ?
......
......@@ -22,7 +22,6 @@ package zodb
import (
"bytes"
"errors"
"fmt"
pickle "github.com/kisielk/og-rek"
......@@ -36,41 +35,8 @@ import (
// https://github.com/zopefoundation/ZODB/blob/a89485c1/src/ZODB/serialize.py
//
// for format description.
//
// PyData can be decoded into PyObject.
type PyData []byte
// PyObject represents persistent Python object.
//
// PyObject can be decoded from PyData.
type PyObject struct {
PyClass pickle.Class // python class of this object
State interface{} // object state. python passes this to pyclass.__new__().__setstate__()
}
// Decode decodes raw ZODB python data into PyObject.
func (d PyData) Decode() (*PyObject, error) {
p := pickle.NewDecoder(bytes.NewReader([]byte(d)))
xklass, err := p.Decode()
if err != nil {
return nil, fmt.Errorf("pydata: decode: class description: %s", err)
}
klass, err := normPyClass(xklass)
if err != nil {
return nil, fmt.Errorf("pydata: decode: class description: %s", err)
}
state, err := p.Decode()
if err != nil {
return nil, fmt.Errorf("pydata: decode: object state: %s", err)
}
return &PyObject{PyClass: klass, State: state}, nil
}
// ClassName returns fully-qualified python class name used for object type.
//
// The format is "module.class".
......@@ -83,50 +49,23 @@ func (d PyData) ClassName() string {
return "?.?"
}
klass, err := normPyClass(xklass)
if err != nil {
return "?.?"
}
return klass.Module + "." + klass.Name
}
var errInvalidPyClass = errors.New("invalid py class description")
// normPyClass normalizes py class that has just been decoded from a serialized
// ZODB object or reference.
func normPyClass(xklass interface{}) (pickle.Class, error) {
// class description:
//
// - type(obj), or
// - (xklass, newargs|None) ; xklass = type(obj) | (modname, classname)
if t, ok := xklass.(pickle.Tuple); ok {
// t = (xklass, newargs|None)
if len(t) != 2 {
return pickle.Class{}, errInvalidPyClass
if len(t) != 2 { // (klass, args)
return "?.?"
}
// XXX newargs is ignored (zodb/py uses it only for persistent classes)
xklass = t[0]
if t, ok := xklass.(pickle.Tuple); ok {
// t = (modname, classname)
// py: "old style reference"
if len(t) != 2 {
return pickle.Class{}, errInvalidPyClass
}
modname, ok1 := t[0].(string)
classname, ok2 := t[1].(string)
if !(ok1 && ok2) {
return pickle.Class{}, errInvalidPyClass
return "?.?" // (modname, classname)
}
return pickle.Class{Module: modname, Name: classname}, nil
return fmt.Sprintf("%s.%s", t...)
}
}
if klass, ok := xklass.(pickle.Class); ok {
// klass = type(obj)
return klass, nil
return klass.Module + "." + klass.Name
}
return pickle.Class{}, errInvalidPyClass
return "?.?"
}
......@@ -30,8 +30,6 @@ type _PyDataClassName_TestEntry struct {
className string
}
// XXX + test with zodbpickle.binary (see 12ee41c4 in ZODB)
func TestPyClassName(t *testing.T) {
for _, tt := range _PyData_ClassName_Testv {
className := PyData(tt.pydata).ClassName()
......@@ -41,7 +39,3 @@ func TestPyClassName(t *testing.T) {
}
}
}
func TestPyDecode(t *testing.T) {
// XXX
}
......@@ -83,7 +83,7 @@ func (xid Xid) XFmtString(b xfmt.Buffer) xfmt.Buffer {
*/
// parseHex64 decodes 16-character-wide hex-encoded string into uint64
// parseHex64 decodes 16-character-wide hex-encoded string into uint64.
func parseHex64(subj, s string) (uint64, error) {
// XXX -> xfmt ?
// XXX like scanf("%016x") but scanf implicitly skips spaces without giving control to caller and is slower
......
// Copyright (C) 2016-2017 Nexedi SA and Contributors.
// Copyright (C) 2016-2018 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
......@@ -26,27 +26,39 @@
//
// Data model
//
// Tid, Oid, ? Xid.
//
//
// Operation
//
// Load(oid, at), iteration.
// OpenStorage
// A ZODB database is conceptually modeled as transactional log of changes to objects.
// Oid identifies an object and Tid - a transaction. A transaction can change
// several objects and also has metadata, like user and description, associated
// with it. If an object is changed by transaction, it is said that there is
// revision of the object with particular object state committed by that transaction.
// Object revision is the same as tid of transaction that modified the object.
// The combination of object identifier and particular revision (serial)
// uniquely addresses corresponding data record.
//
// Tids of consecutive database transactions are monotonically increasing and
// are connected with time when transaction in question was committed.
// This way, besides identifying a transaction with changes, Tid can also be
// used to specify whole database state constructed by all cumulated
// transaction changes from database beginning up to, and including,
// transaction specified by it. Xid is "extended" oid that specifies particular
// object state: it is (oid, at) pair that is mapped to object's latest
// revision with serial ≤ at.
//
// Python data
// Object state data is generally opaque, but is traditionally based on Python
// pickles in ZODB/py world.
//
// PyData, PyObject, ...
//
// Operations
//
// Storage drivers
// A ZODB database can be opened with OpenStorage. Once opened IStorage
// interface is returned that represents access to the database. Please see
// documentation of IStorage, and other interfaces it embeds, for details.
//
// IStorageDriver, RegisterDriver + wks (FileStorage, ZEO and NEO).
//
// ----
// --------
//
// XXX link to zodbtools / `zodb` cmd?
// See also package lab.nexedi.com/kirr/neo/go/zodb/zodbtools and associated
// zodb command that provide tools for managing ZODB databases.
package zodb
import (
......@@ -88,7 +100,7 @@ type Oid uint64
//
// At specifies whole database state at which object identified with Oid should
// be looked up. The object revision is taken from latest transaction modifying
// the object with tid <= At.
// the object with tid At.
//
// Note that Xids are not unique - the same object revision can be addressed
// with several xids.
......@@ -306,7 +318,7 @@ type IDataIterator interface {
// ---- misc ----
// Valid returns whether tid is in valid transaction identifiers range
// Valid returns whether tid is in valid transaction identifiers range.
func (tid Tid) Valid() bool {
// NOTE 0 is invalid tid
if 0 < tid && tid <= TidMax {
......@@ -316,7 +328,7 @@ func (tid Tid) Valid() bool {
}
}
// Valid returns true if transaction status value is well-known and valid
// Valid returns true if transaction status value is well-known and valid.
func (ts TxnStatus) Valid() bool {
switch ts {
case TxnComplete, TxnPacked, TxnInprogress:
......
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