3.7: (2007-05-02) * Fix a bug in BTree insert_item(). The bug was discovered by Andrew Bettison, and he contributed the patch to fix it. The exact change can be seen by comparing the BNode insert_item() implementation in the 3.7 and 3.6 releases. Thanks, Andrew. * Make bulk_load safer by consuming the entire response from the server before yielding any results. Also provide additional checks on oids list that is passed in. * Add keyword arguments to the BTree range iteration methods to allow the caller to include or exclude items on the boundary of the range. * Add Connection.get_load_count(), which returns the number of times any object's state has been loaded. The idea is to provide some information that applications could use to make cache size adjustments at run time. * Repair docstring on FileStorage.sync(). * Fix a bug in Connection.get_crawler() that caused loaded objects to be included twice in the generated sequence. * Rename low-level conversion functions. p64 -> int8_to_str p32 -> int4_to_str u64 -> str_to_int8 u32 -> str_to_int4 Add utility functions: read() write() read_int4() write_int4() read_int8() write_int8() read_int4_str() write_int4_str() read_int8_str() write_int8_str() Run file and socket io through these utils. A module variable durus.utils.TRACE can be set to trace all I/O, as it runs through read()/write(). * Add ShortRead, a subclass of IO that is raised when read() can't find the expected number of bytes. * Add IntSet, a class for compact representation of sets of natural numbers (such as the set of all oids in a Durus database). This may be useful for packing of large databases, where traversal requires keeping track of the set of oids already visited. * Add a File class for use by FileStorage. The intent is to provide methods that wrap platform-specific differences in locking, syncing, renaming, etc. * Add data array classes to utils. Byte, BitArray, ByteArray, WordArray, and IntArray. The objective of this is to provide support holding large sets/arrays either either in memory or on disk by wrapping file-like objects that hold the bytes. * Add a new, experimental, type of FileStorage. The new type of FileStorage is called ShelfStorage. It stores the offset index (at the time of the last pack) using an array format that can be used directly from disk. This makes the storage start-up time and memory requirement vary with the number of objects saved since the last pack, whereas in the standard FileStorage format (FileStorage2), the start-up time and the memory vary with the total number of objects stored in the database. The new shelf.py module can be used as a script to convert an existing FileStorage into the new format. ShelfStorage is still new and it has not been tested nearly as well as FileStorage. Please keep that in mind if you try it. * Don't depend on FileStorage readonly flag for History. Just make end() raise a RuntimeError. * Unlink rename target if it exists. * Use File class for wrapping the FileStorage file. Locking is delayed until write, or other destructive operations are attempted. Don't save "repair" value on an attribute. Don't set fp to None when close() is called. * If an old ".pack" file is found, clobber it instead of raising an exception. The old file is assumed to be left from a previous pack that was stopped before completion. * Call close() on graceful quit. * Avoid leaving tempfiles around when tests execute. Make test socket be in /tmp, to avoid permissions problems. * Make extract_class_name() guard against bogus records. Otherwise, a bogus record will crash a server that uses extract_class_name() for logging. * Make stop_durus() attempt to wait until the server stops listening. * Improve load record format: include pid. * Add some assertions that guard against duplicate oid allocation. * Remove durus.utils.format_oid(). Use obj._p_format_oid() instead. 3.6: (2006-11-15): * Add support for persistent instances that use slots for state data. Separate the Persistent class implementation into two classes, so that Persistent inherits from a new class named 'PersistentObject'. The PersistentObject defines a __slots__ value without including '__dict__', so it is possible to have PersistentObject instances that don't have '__dict__' values, which can reduce the amount of RAM required to hold those instances. Use PersistentObject instead of Persistent as the base class for ComputedAttribute, PersistentDict, PersistentList, PersistentSet, BTree, and BNode and define __slots__ on these classes for the attributes that they actually use. This means that instances that have any of these as the __class__ will no longer support arbitrary attribute names. For subclasses of any of these classes, arbitrary attribute names are still be supported. * Change __setstate__() so that it clears the existing state before setting the new state. __setstate__({}) or __setstate__(None) is expected to clear the data attributes of any persistent instance. * Add support a "root_class" keyword arg to the Connection constructor. Make the root as an instance of this class (or PersistentDict if the root_class value is None). If the root_class value is given, verify that it really is the class of the root instance. * When making a new root instance, obtain the oid from the storage as you would for any persistent instance. The first call to Storage.new_oid() must now return the expected ROOT_OID. This establishes the invariant that every oid of a valid record that a Storage is asked to store has an existing record or else it has been returned by a call to the new_oid() method. * When handling invalidations, immediately make ghosts of loaded instances that have not already been accessed during this transaction. Previously, the oids of these objects were just saved in the invalid_oids set, and this could lead to an AssertionError if commit() is called before abort(). * Hold a hard reference to the root instance to keep it in the cache. * Move PICKLE_PROTOCOL to a global constant in the serialize module. * Add some low-level attribute-manipulation methods to the persistent module. These allow attribute manipulation that does not trigger changes in the persistent state, even when using the C-implementation of PersistentBase. * Update HistoryConnection. Don't depend on the existence of a __dict__ to signal that an object exists in the current transaction. * Call _p_set_status_unsaved() in _p_note_change() instead of doing the same thing in-line. This allows subclasses that override _p_set_status_unsaved() to have those calls happen on every (normal) transition to the unsaved state. * Use a deque in gen_oid_storage so that we can call popleft() instead of pop(0). * When a start_oid is provided as an argument to gen_oid_record(), it now yields (oid, record) pairs for objects that are reachable from the given start record. Consequently, this code can be shared by the FileStorage pack and the Connection crawler methods. * Make WriteConflictError a separate class, so that read and write conflicts can be explicitly distinguished. When attempts to read or or commit are made after a conflict has been raised, but before abort() has been called, raise an AssertionError instead of a new ConflictError. * Add ObjectDictionary class, used for holding the references in the cache. This is like a WeakValueDictionary, except that the actual deleting of oids is delayed until the next time there is an iteration, which is for us at the time of the next call to shrink(). * Add BTree.note_change_of_bnode_containing_key() so that code managing BTrees of non-persistent containers can be easier to understand. * Change Connection.get() so that, when it pulls an object record from storage, it goes ahead and loads the object state. The idea is that calls to get() will almost always be followed by attribute accesses on the returned object, so we should avoid the need to retrieve the object record again. Note that all Connections use get() to retrieve the root instance. This change means that the root state is loaded immediately. * Avoid potential KeyError when a reference has died but the key has not yet been removed from the WeakValueDictionary. * Change wait_for_server so that maxtries can be bigger than maxint. * Add BTree.update(). * Make default get_packer() return None instead of raising NotImplementedError. * Pass a handle_invalidations argument to self.storage.end(). If there is not an incremental packer, call the non-incremental one. * Track unused allocated oids for each client. Make sure that records submitted by one client do not use oids that have been allocated, but not used, by another client. * Add support for the storage server to use a Storage that may report invalidations. * Add command line options that allow starting a server that is a client of another server. * Remove the FileStorage format conversion script. * Remove support for FileStorage1 format. * Change FileStorage.sync() so that, on the first call after a pack, it returns the list of oids removed by the pack. * Improve error message shown if run_durus.py fails. * Run socket output through a sendall() function, just as input goes through a recv() function. Add a TRACE global. If TRACE is true, the socket traffic is printed to stdout. Treat IOError like ClientError. * Add unit tests for PersistentList and PersistentDict. * Make PersistentDict.update() work with a variety of argument patterns, like dict.update(). * Revise implementation of PersistentDict.copy(). * Make sure that the client does not wait for reply status in the unlikely event that it has requested to commit an empty transaction. * Remove InvalidObjectReferenceError. Just use ValueError. * Add a default Storage.close() method. 3.5: (2006-08-16): * Fix a bug introduced in version 3.4 that could, under certain conditions, allow conflicts to be missed. In particular, if the last strong reference to a Persistent instance was removed, a conflict involving that instance would be missed. The fix involves changing the Persistent.__getattr__ so that it calls the 'note_access' method on the Connection. This method creates a strong reference to the Persistent instance by adding it to a 'recent_objects' set. Objects are eventually removed from the recent_objects set when they are converted into ghosts in the cache's shrink() method. Since a set() is used for recent_objects, all Persistent instances *must* now be hashable. PersistentSet instances were not, and that has been changed. * Revise the cache code. It now uses a WeakValueDict instead of a plain dict to hold the references. This simplifies the code because we no longer need to call the weakref instances directly. It also helps the cache shrinking loop because the weakref callbacks have an immediate impact on the size of the mapping. * Remove the ghost_fraction attribute from the Cache. It is no longer used in shrink(). * Rename the Persistent '_p_touched' attribute to '_p_serial'. This is set, on every attribute access, to the value of the Connection's 'transaction_serial' attribute (which was formerly named 'sync_count'). * Add a bulk_load() method to Storage. The ClientStorage implementation reduces avoids latency delays be retrieving many object records with a single request to the server. * Add Connection.get_crawler(). This returns a generator for the sequence of objects reachable from a given start OID. The crawler uses the new bulk_load() method for speed. It can be used, with some care, to initialize the object cache. * Remove Connection.cache_get() and Connection.cache_set(). * Use set instead of Set throughout. This means that Durus now requires Python version >= 2.4. * Add the ability to set the owner, group, and umask when a unix domain socket is used for a server. * Attempt to clean up stale socket files when starting a server on a unix domain socket. * Move some repeated code related to addresses and sockets into a SocketAddress class and subclasses HostPortAddress and UnixDomainSocketAddress. * In the server, add support for a protocol verification command. Use this in the client constructor to allow orderly behavior if the client and the server do not implement the same protocol. * Add a server command for allocating blocks of oids. * Add client support for maintaining and drawing from a pool of oids allocated by the server. This reduces the number of commands that must be sent to the server during a commit. * Add support for recycling allocated oids when there is a conflict during a commit. * Make sure that the FileStorage constructor can work if the named file exists, but is empty. * Initialize sync_count to 1 so that new ghosts, for which _p_touched is initialized to 0, never appear to have been accessed since the last transaction. * Move some logic used for unpickling references to the connection cache so that it can be faster. Add Cache.get_instance() for this purpose. Add Connection.get_cache() so that the ObjectReader can use it. 3.4.1: (2006-05-18): * Fix a memory leak that was in the 3.4 tarball until 2006-05-12. * Fix doc string errors. * Fix initialization of _p_touched in python version of PersistentBase to agree with the C implementation. 3.4: (2006-05-11): 28347 * Refine the conflict avoidance and cache aging behavior. Now conflicts don't occur unless there is an invalid object for which this Connection has actually accessed an attribute since the last call to commit() or abort(). The Connection saves a "sync_count" on every commit() or abort(). On every access to (an ordinary) attribute of a Persistent instance, the _p_touched is set to be the Connection's sync_count. To make this possible without any significant performance penalty, the _p_connection and other '_p_' attributes are moved from Persistent to PersistentBase and implemented in C. Also, a ConnectionBase class is implemented in C so that the sync_count, which is needed so frequently, can be accessed directly in the C implementation of PersistentBase. Since we now know which instances have actually been accessed since the last commit() or abort(), the Connection no longer need to maintain the set of loaded_oids. The cache manager can use the _p_touched to distinguish less recently used instances. The Cache class has a new ghost_fraction attribute. The value of this attribute defaults to 0.5 and can be any number between 0 and 1. Higher values make the cache more aggressive about ghosting objects as it tries to reduce the cache size. The Cache "held" attribute is removed, along with the hold() method. * Added a history.py module that defines HistoryConnection, a Connection subclass that supports time-travel in a read-only FileStorage file. The class provides next() and previous() methods for stepping among the stored transactions. It also provides next_instance(obj) and previous_instance(obj) for moving to to a transaction where obj has a state that is different from the current state. Note that packing a FileStorage consolidates the transactions, so the HistoryConnection can only move among the transactions since the last pack. * Make the durus client run in a __console__ module. This makes it behave a little more like the regular Python interpreter. * Add support for running the durus client/server connections through unix domain sockets. The ClientStorage and StorageServer constructors accept an "address" keyword argument. If the address value can be a (host, port) tuple or else a string giving a path use for the unix domain socket. The separate "host" and "port" keyword parameters are still supported, but they may be removed in future releases. If your code calls these constructors, please change it to use the "address" keyword. * Change the recv() function used in the client/server to read in chunks of at most 1 million bytes. This avoids a malloc error observed when running test/stress.py on an OS X machine with python 2.4.2. * Make the durus server a little tougher. If it gets an unknown command, it now logs the error and closes the connection instead of crashing. * Add Storage.pack() and Storage.get_size(). 3.3: (2006-03-15): 28065 * Keep strong references to objects until we decide that they haven't been recently touched. This limits the impact of the Python garbage collector. * Change the FileStorage class as needed to agree with the magic header string found in an existing file. Do this no matter which of the constructors (FileStorage, FileStorage1, or FileStorage2) is called to create the instance. Before this change, opening an an existing file with the FileStorage2 constructor (instead of the generic FileStorage constructor), raised an exception. * Adjust logging. Before this change, the server logs the class of each object when it is loaded if the logginglevel is at level 5 or below. This changes that threshhold to level 4. Now, when the logging level is 5 or below, the server prints a census of objects loaded since the last commit or abort. This can make it easier to understand patterns of cache misses. * Add dummy new_oid() and get_packer() methods to the Storage class for documentation. 3.2: (2006-02-01): r27892 * Add 5 iteration methods to BTree. __reversed__() for reverse iteration of keys. items_backward() for reverse iteration of items. items_from() for iterations starting at a given key. items_backward_from() for reverse iterations from a given key. items_range() for iterations of items with keys in a given range. * Add __nonzero__ and setdefault methods to BTree * Change the name of BTree's get_count method to __len__. * Add setuptools support (when installed) to setup.py. * Remove convert_zodb.py script, rather than fix/maintain it. 3.1: (2005-10-18): r27556 * Add PersistentSet. (Applications that use the persistent_set must use Python versions >= 2.4). * Add MemoryStorage, an in-memory storage for testing purposes. 3.0: (2005-09-08): r27334 * Fix bug in utility function (touch_every_reference()) added in 3.0a. * Replace ._p_changed = 1 to ._p_note_change() in btree.py. 3.0a: (2005-08-09): r27118 * Revise packed record format to write records where the instance state is compressed using zlib. This reduces the size of stored files and the number of bytes of data transmitted during load/store operations. * Add a FileStorage2 file format. The new format does not need or store transaction identifiers. It also includes a pre-built index of the object records written at the time of the last pack. This results in a faster start-up. Conversion is not automatic, though. A convert_file_storage.py is included with this release to make it simple to convert a file to either format. * Add stress testing client stress.py. * When the state of a ghost can't be loaded because it has been removed by a pack (from another connection), make the error be a ReadConflictError instead of a DurusKeyError. * Implement an incremental pack. The storage server can now serve clients while packing the database. * Add gen_every_instance() utility to durus.connection. * Add touch_every_reference() utility function to durus.connection. * Remove the use of tids from Connection, ClientStorage, and StorageServer. The StorageServer now sends STATUS_INVALID for requests to load object records that are known to be invalid for that client. Change Storage.load() so that the return value is a tid-less object-record. The client/server protocol also changes to stop transmitting tids as part of the response for sync and commit requests. Storage.sync() now returns only a list of oids. Storage.end() now returns None. 2.0 (2005-4-26) r26653: * The only change is in the license. The new license is the GPL-compatible version of the CNRI Open Source License. 1.5 (2005-3-07) r26296: * A small change makes Persistent instances pickle-able. 1.4 (2005-1-14) r25851: * Revise serialize.py to avoid using cPickle.noload(), which can't handle a dict subclass. * Add a --startup option to the durus command line tool. 1.3 (2004-12-10) r25746: * Use 'b' in file modes. * Continue to rename the open packed file on POSIX systems. * Convert tests to sancho's new utest format. * Improve test coverage for FileStorage. * Lower the priority of Sync logging. * Show host and port when client fails to connect. 1.2 (2004-09-08) r25044: * Add durus command line tool. * Add btree module. * Update pack() so that it works on Windows and so that it gets a lock again after the pack is completed. 1.1 (2004-08-05) r24872: * Provide a close() method for FileStorage. Call appropriate win32 unlock function. * Remember to lock the new file created by pack(). Unlock the old file. 1.0 (2004-07-31) r24846: * Fix obscure bug in storage_server during logging. * Repaired example in README.txt. * Added FAQ. * Made ProtocolError inherit from DurusError. * Added fsync() after writing transaction data. 0.1 (2004-07-27) r24791: * Initial Release