Commit f6a27a1e authored by Kirill Smelkov's avatar Kirill Smelkov

go/zodb: IPersistent + Connection stub

Add to ZODB/go IPersistent - the interface that is used to represent
in-RAM application-level objects that are mirroring objects in database.

The interface is modelled after Python's IPersistent

	https://github.com/zopefoundation/ZODB/blob/3.10.7-4-gb8d7a8567/src/persistent/interfaces.py#L22

but is not exactly equal to it. In particular we support concurrent
access to an object from multiple goroutines simultaneously.

Due to concurrency support there is no STICKY state, because STICKY is
used in CPython version to temporarily pin object in RAM briefly and is
not safe to use from multiple threads there. Correspondingly the
semantic of PActivate is a bit different from _p_activate - in Go, after
an object has been activated, it is guaranteed that it will remain
present in RAM until it is explicitly deactivated by user.

Please see details of the activation protocol in IPersistent
documentation.

ZODB/py uses interface (IDataManager) for a persistent-object's jar, but
in Go I decided, at least for now, to go without explicit interface at
that level. For this reason a concrete type - Connection - will be used,
and so its stub is also introduced in the patch, since IPersistent wants
to return the connection via PJar.
parent 9b751272
// Copyright (C) 2018 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
package zodb
// database connection.
import (
"context"
"lab.nexedi.com/kirr/go123/mem"
)
// Connection represents a view of a ZODB database.
type Connection struct {
stor IStorage // underlying storage
at Tid // current view of database; stable inside a transaction.
}
// load loads object specified by oid.
func (conn *Connection) load(ctx context.Context, oid Oid) (_ *mem.Buf, serial Tid, _ error) {
return conn.stor.Load(ctx, Xid{Oid: oid, At: conn.at})
}
// Copyright (c) 2001, 2002 Zope Foundation and Contributors.
// All Rights Reserved.
//
// Copyright (C) 2018 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This software is subject to the provisions of the Zope Public License,
// Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
// THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
// WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
// FOR A PARTICULAR PURPOSE
package zodb
// IPersistent.
import (
"context"
)
// IPersistent is the interface that every in-RAM object representing any database object implements.
//
// It is based on IPersistent from ZODB/py:
//
// https://github.com/zopefoundation/ZODB/blob/3.10.7-4-gb8d7a8567/src/persistent/interfaces.py#L22
//
// but is not exactly equal to it.
//
// It is safe to access IPersistent from multiple goroutines simultaneously.
//
// Use Persistent as the base for application-level types that need to provide persistency.
type IPersistent interface {
PJar() *Connection // Connection this in-RAM object is part of.
POid() Oid // object ID in the database.
// object serial in the database as of particular Connection (PJar) view.
// InvalidTid if not yet loaded.
PSerial() Tid
// PActivate brings object to live state.
//
// It requests to persistency layer that in-RAM object data to be present.
// If object state was not in RAM - it is loaded from the database.
//
// On successful return the object data is either the same as in the
// database or, if this data was previously modified by user of
// object's jar, that modified data.
//
// Object data must be accessed only after corresponding PActivate
// call, which marks that object's data as being in use.
PActivate(ctx context.Context) error
// PDeactivate indicates that corresponding PActivate caller finished access to object's data.
//
// As PActivate makes sure object's data is present in-RAM, PDeactivate
// tells persistency layer that this data is no longer used by
// corresponding PActivate caller.
//
// Note that it is valid to have several concurrent uses of object
// data, each protected with corresponding PActivate/PDeactivate pair:
// as long as there is still any PActivate not yet compensated with
// corresponding PDeactivate, object data will assuredly stay alive in RAM.
//
// Besides exotic cases, the caller thus must not use object's data
// after PDeactivate call.
PDeactivate()
// PInvalidate requests in-RAM object data to be discarded.
//
// Irregardless of whether in-RAM object data is the same as in the
// database, or it was modified, that in-RAM data must be forgotten.
//
// PInvalidate must not be called while there is any in-progress
// object's data use (PActivate till PDeactivate).
//
// In practice this means that:
//
// - application must make sure to finish all objects accesses
// before transaction boundary: at transaction boundary - either
// at abort or commit, the persistency layer will sync to
// database and process invalidations.
//
// - if PInvalidate is explicitly called by application, the
// application must care to make sure it does not access the
// object data simultaneously.
PInvalidate()
// PModify marks in-RAM object state as modified.
//
// It informs persistency layer that object's data was changed and so
// its state needs to be either saved back into database on transaction
// commit, or discarded on transaction abort.
//
// The object must be already activated.
//PModify() TODO
// XXX probably don't need this.
//PState() ObjectState // in-RAM object state.
}
// ObjectState describes state of in-RAM object.
type ObjectState int
const (
GHOST ObjectState = -1 // object data is not yet loaded from the database
UPTODATE ObjectState = 0 // object is live and in-RAM data is the same as in database
CHANGED ObjectState = 1 // object is live and in-RAM data was changed
// no STICKY - we pin objects in RAM with PActivate
)
...@@ -60,6 +60,42 @@ ...@@ -60,6 +60,42 @@
// Please see IStorage, and interfaces it embeds, for details. // Please see IStorage, and interfaces it embeds, for details.
// //
// //
// Application layer
//
// The application layer provides access to a ZODB database in terms of in-RAM
// application-level objects whose in-RAM state is synchronized with data in the database. For
// the synchronization to work, objects must be explicitly activated before
// access (contrary to zodb/py where activation is implicit, hooked into
// __getattr__), for example:
//
// var obj *MyObject // *MyObject must implement IPersistent
// ... // init obj pointer, usually by traversing from another persistent object.
//
// // make sure object's in-RAM data is present.
// //
// // ZODB will load corresponding data and decode it into obj.
// // On success, obj will be live and application can use its state.
// err := obj.PActivate(ctx)
// if err != nil {
// return ... // handle error
// }
//
// obj.xxx // use object.
// if ... {
// obj.xxx++ // change object.
// obj.PModify() // let persistency layer know we modified the object.
// }
//
// // tell persistency layer we no longer need obj's in-RAM data to be present.
// // if obj was not modified, its in-RAM state might go away after.
// obj.PDeactivate()
//
// IPersistent interface describes the details of the activation protocol.
//
// Object activation protocol is safe to access from
// multiple goroutines simultaneously.
//
//
// Storage drivers // Storage drivers
// //
// To implement a ZODB storage one need to provide IStorageDriver interface and // To implement a ZODB storage one need to provide IStorageDriver interface and
......
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