Presentation is loading. Please wait.

Presentation is loading. Please wait.

Persistent Programming with ZODB 10 th International Python Conference Alexandria, Virginia Jeremy Hylton and Barry Warsaw

There are copies: 3
What is Persistence? Automatic management of persistent storage Frees programmer from writing code to dump objects into files Allows programmer to focus.

(Very) Simple Group Calendar Calendar which can display a whole month, or a single day, with events Can create new appointments with rendezvous information.

Transaction Intro slide XXX New builtin get_transaction() Returns transaction for current thread ZEO manages transactions across processes, machines Transactions.

Similar presentations


Presentation on theme: "Persistent Programming with ZODB 10 th International Python Conference Alexandria, Virginia Jeremy Hylton and Barry Warsaw"— Presentation transcript:

1 Persistent Programming with ZODB 10 th International Python Conference Alexandria, Virginia Jeremy Hylton and Barry Warsaw {jeremy,barry}@zope.com

2 What is Persistence? Automatic management of object storage –Persists across program invocations Frees programmer from writing code to dump objects into files Allows programmer to focus on object model for application

3 ZODB Approach to Persistence Minimal impact on existing Python code (transparency) Serialization (pickle) to store objects Transactions to control updates Pluggable backend storages to write to disk

4 Alternatives to ZODB Many: –flat files, relational database, structured data (XML),BerkeleyDB, shelve Each has limitations –Seldom matches app object model –Limited expressiveness / supports few native types –Requires explicit app logic to read and write data

5 ZODB -- the Software Object database for Zope –Designed by Jim Fulton –Started as BoboPOS Extracted for non-Zope use –Andrew Kuchling Source release w/distutils from Zope Corp. –Dec 2001 Wiki: http://www.zope.org/Wikis/ZODBhttp://www.zope.org/Wikis/ZODB –info central for ZODB

6 Software architecture StandaloneZODB packages –Persistence, ZODB, ZEO –ExtensionClass, sundry utilities ZODB contains –DB, Connection –Several storages Compatibility –Runs with Python 2.0 and higher –Some limitations on Persistent objects No cycle GC, no weak refs, … These are ExtensionClass issues

7 ZODB Architecture (1) Persistent Application Persistence ZODB Database Transaction Storage

8 Public Components Components with public APIs –Database allows application to open connections connection: app interface for accessing objects –Transaction: app interface for making changes permanent –Persistent base class Logically distinction from ZODB

9 Internal Components Storage –manage persistent representation on disk ZEO –Share storage among multiple processes, machines

10 Future ZODB Architecture ZODB4 will isolate components –Persistence, Transaction interfaces separate –Database, Storage stay in ZODB Advantages –Allows other databases, e.g. object- relational mapping –Use Persistence, Transaction without ZODB

11 Key ZODB Concepts Persistence by reachability Transactions Resource management –Multiple threads –Memory and caching

12 Persistence by Reachability A objects reachable from root stored in database –Root mapping provided by database Each persistent object stored independently –use pickle –all non-persistent attributes included –customize with __getstate__()

13 Transactions Coordinate update of objects –Modified objects associated with transaction –Commit makes modification persistent –Abort reverts to previous state Means to cope with failure –Conflicting updates –Something goes wrong with system

14 Resource Management Threads –One thread per transaction –Controlled sharing via transactions Memory –Database contains many objects Too many to fit in memory –Objects moved in and out of memory ZODB manages this automatically Knobs exposed to applications

15 (Very) Simple Group Calendar Calendar which can display a whole month, or a single day, with events Can create new appointments with rendezvous information Can invite people to an appointment

16 Group Calendar Objects Calendar – holds appointments keyed by subject and date (sorted) Person – name, core hours ; later updated to just username, realname Appointment – holds date, duration, subject, location, list of participants (driver script)

17 Required imports import ZODB from ZODB.DB import DB from ZODB.FileStorage import FileStorage from Persistence import Persistent from BTrees.OOBTrees import OOBTrees

18 Creating persistent classes from Persistence import Persistent class Person(Persistent): # …

19 Application boilerplate fs = FileStorage(cal.fs) db = DB(fs) conn = DB.open() root = conn.root() if not root.has_key(collectionName): root[collectionName] = OOBTree()

20 Using transactions calendar = root[calendar] calendar.add_appointment(app) get_transaction().commit() # …or… get_transaction().abort()

21 Writing persistent classes XXX Persistent if reachable from the root Persistency by storing/loading pickles ZODB must know when an object is accessed or changed Automatic (transparent) for attribute access Some common Python idioms require explicit interactions

22 Persistence by reachability XXX Persistent object must be reachable from the root object, which ZODB creates automatically myPersistentObj = PersistentObj() conn.root()[coll] = myPersistentObject

23 What state is saved? Objects to be stored in ZODB must be picklable. Normally, an objects attributes (__dict__) are pickled by ZODB for storing, and the pickle is unserialized into __dict__ for loading. Persistent captures all attribute access via __getattr__() and __setattr__() hooks

24 References to other objects Subobjects are pickled by value, except: –Pickled by reference if suboject is persistent –Classes, modules, and functions pickled by fully qualified name –Upon unpickling instances, __init__() is not called unless the class defines an __getinitargs__() method. –See the Python 2.2 docs for pickle module for more rules regarding extension types, etc.

25 Mutable attributes Mutable subobject that is not Persistent –Builtin types (list, dict), instances Changes not caught by ZODB –__getattr__() only works for parent –Must mark parent as changed >>> person.appointments [] >>> person.appointments.append(app) >>> person._p_changed = 1

26 PersistentMapping Class provided by ZODB for persistent, near-dictionary-like semantics It fiddles with _p_changed for you: >>> person.contacts >>> person.contacts[Barry] = barry >>> get_transaction().commit()

27 PersistentList? Not part of Zope Corps distribution (yet) Andrew Kuchlings SourceForge project has one (zodb.sf.net) Provides list-like semantics while taking care of _p_changed fiddling

28 Unpicklable objects class F(Persistent): def __init__(self, filename): self.fp = open(filename) >>> root[files] = F(/etc/passwd) >>> get_transaction().commit() … cPickle.UnpicklableError: cannot pickle objects

29 Unpicklable objects class F(Persistent): def __init__(self, filename): self.fp = open(filename) def __getstate__(self): return self.fp.name def __setstate__(self, filename): self.fp = open(filename) >>> root[files] = F(/etc/passwd) >>> get_transaction().commit() >>>

30 Volatile attributes Attributes not to be stored persistently should be prefixed with _v_ class F(Persistent): def __init__(self, filename): self._v_fp = open(filename) >>> root[files] = F(/etc/passwd) >>> get_transaction().commit() # later… >>> root[files].__dict__ {}

31 Python special methods ExtensionClass has some limits –post-1.5.2 methods –Reversed binops, e.g. __radd__ –Older versions had some problems with __cmp__() and (maybe) __hash__() XXX Not fundamental to approach –Future implementation will not use E.C.

32 Managing object evolution Methods and data can change –Add or delete attributes –Methods can also be redefined Classes stored by reference –Instances gets whatever version is imported __get/setstate__() can handle data –Provide compatibility with old pickles, or –Update all objects to new representation

33 __setstate__() class Person(Persistent): def __init__(self, name): self.name = name >>> barry = Person(Barry Warsaw) >>> root[people][barry] = barry >>> get_transaction().commit()

34 __setstate__() cont class Person(Persistent): def __init__(self, username, realname): self.username = username self.realname = realname def __setstate__(self, d): self.realname = name = d[name] username = name.split()[0].lower() self.username = username

35 Transaction Intro slide XXX New builtin get_transaction() Returns transaction for current thread ZEO manages transactions across processes, machines Transactions & Connections –Independent, consistent views of DB

36 ACID properties Atomic – All updates performed, or none Consistent –Responsibility of application –Changes should preserve object invariants Isolated –Each transaction sees consistent state –Transactions occur in serializable order Durable –After a commit, change will survive crash

37 -Concurrency control Optimistic concurrency vs. locking –ZODB assumes many more reads than writes –No locking of objects; most writes succeed so dont need to pay overhead of locking –However, conflicts can occur on writes (and in a ZEO environment, on reads too) –Conflict resolution is the applications responsibility

38 Transaction boundaries Under application control Transaction begin is implicit get_transaction().commit() get_transaction().abort()

39 Write conflicts Transactions must be serializable Two transactions change object concurrently –Only one change can succeed –Other raises ConflictError on commit() Handling ConflictError –Abort transaction, and retry –Application-level conflict resolution

40 Conflicts and Consistency New method on Calendar object def make_appointment(self, apt, attendees): self.add_appointment(apt) for person in attendees: if person.is_available(apt.date, apt.duration): person.add_appointment(apt) apt.add_person(person) –Guarantees appointments dont conflict Consider two calls at same time –Data race on is_available()? –Conflict raised when object commits

41 Conflict Example def update1(cal, attendees): apt = Appointment(refrigerator policy, Time(2/5/2002 10:00), Time(0:30)) cal.make_appointment(apt, attendees) def update2(cal, attendees): apt = Appointment(curly braces, Time(2/5/2002 10:00), Time(1:00)) cal.make_appointment(apt, attendees) Two calls at once results in one error Traceback (most recent call last): File, line 1, in ? File ZODB/Transaction.py, line 233, in commit File ZODB/Connection.py, line 347, in commit File ZODB/FileStorage.py, line 634, in store ConflictError: database conflict error (serial was 034144749675e55d, now 03414442940c1bdd)

42 Object serial numbers Every object has a serial number –monotonically increasing –might be a timestamp –a.k.a. revision number, transaction id Upon commit, current serial number is compared to stored serial number. If they differ, object has been modified externally in the interim: ConflictError is raised Upon successful write, object gets a new serial number

43 Dealing w/write conflicts Depends on the application! from ZODB.POSException import ConfictError while 1: try: obj.counter += 1 get_transaction().commit() except ConflictError: pass else: break

44 Dealing w/write conflicts cont Whats (possibly) wrong with this picture? obj.counter += 1 while 1: try: get_transaction().commit() except ConflictError: pass else: break

45 Read conflicts with ZEO When using ZEO, it is possible to get ConflictErrors on reading an object –Storage servers send invalidation messages to clients when an object changes –Invalidation messages are only visible at transaction boundaries –Clients read only most current object revision Future: Multi-version concurrency control Now: get_transaction().abort() and try again

46 Application level conflict resolution Objects can implement their own (write) conflict resolution logic Implement _p_resolveConflict() method –args: oid, old serial #, new serial #, current pickle data –returns: new pickle data or None –None signals a ConflictError

47 Subtransactions You can create subtransactions within a main transaction –individually commit and abort subtransactions –not truly committed until containing transaction is committed Primarily for reducing in-memory footprint >>> get_transaction().commit(1)

48

49


Download ppt "Persistent Programming with ZODB 10 th International Python Conference Alexandria, Virginia Jeremy Hylton and Barry Warsaw"

Similar presentations


Ads by Google