[Zodb-checkins] CVS: ZEO - .cvsignore:1.2 LICENSE.txt:1.2 README.txt:1.2 setup.py:1.2 test.py:1.2 CHANGES.txt:1.29

Jeremy Hylton jeremy@zope.com
Tue, 11 Jun 2002 09:43:37 -0400


Update of /cvs-repository/ZEO
In directory cvs.zope.org:/tmp/cvs-serv5548

Modified Files:
	CHANGES.txt 
Added Files:
	.cvsignore LICENSE.txt README.txt setup.py test.py 
Log Message:
Merge ZEO2-branch to trunk.


=== ZEO/.cvsignore 1.1 => 1.2 ===


=== ZEO/LICENSE.txt 1.1 => 1.2 ===
+-----------------------------------------------
+
+This software is Copyright (c) Zope Corporation (tm) and
+Contributors. All rights reserved.
+
+This license has been certified as open source. It has also
+been designated as GPL compatible by the Free Software
+Foundation (FSF).
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions in source code must retain the above
+   copyright notice, this list of conditions, and the following
+   disclaimer.
+
+2. Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions, and the following
+   disclaimer in the documentation and/or other materials
+   provided with the distribution.
+
+3. The name Zope Corporation (tm) must not be used to
+   endorse or promote products derived from this software
+   without prior written permission from Zope Corporation.
+
+4. The right to distribute this software or to use it for
+   any purpose does not give you the right to use Servicemarks
+   (sm) or Trademarks (tm) of Zope Corporation. Use of them is
+   covered in a separate agreement (see
+   http://www.zope.com/Marks).
+
+5. If any files are modified, you must cause the modified
+   files to carry prominent notices stating that you changed
+   the files and the date of any change.
+
+Disclaimer
+
+  THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS''
+  AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+  NO EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE
+  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+  DAMAGE.
+
+
+This software consists of contributions made by Zope
+Corporation and many individuals on behalf of Zope
+Corporation.  Specific attributions are listed in the
+accompanying credits file.


=== ZEO/README.txt 1.1 => 1.2 ===
+
+  Zope Enterprise Objects (ZEO) extends the Zope Object Database
+  (ZODB) to multiple processes, machines, and locations.  It provides
+  scalability, high availability, and distribution for ZODB.  For more
+  information, see the ZEO Web page at http://www.zope.org/Products/ZEO/.
+
+  IMPORTANT: ZEO version 2 is not backwards compatible with ZEO 1.0.
+  A system that uses ZEO must upgrade all clients and servers at the
+  same time.
+
+  The ZEO package is contained in the directory named ZEO.
+
+  If you are using Zope, see doc/ZopeREADME.txt; otherwise, see
+  doc/NonZopeREADME.txt. 


=== ZEO/setup.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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
+# 
+##############################################################################
+from distutils.core import setup
+
+packages = ['ZEO', 'ZEO.zrpc', 'ZEO.tests']
+
+setup(name="ZEO",
+      version="2.0a1",
+      description="Zope Enterprise Objects",
+      maintainer="Zope Corp.",
+      maintainer_email="zodb-dev@zope.org",
+      url = "http://www.zope.org/Products/ZEO",
+
+      packages = packages,
+      )


=== ZEO/test.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+# 
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (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
+# 
+##############################################################################
+"""Test harness for ZEO
+
+usage: python test.py [options] [modulepath] [testcase]
+
+options:
+    -v -- verbose (can be repeated to increase verbosity)
+    -b -- run "setup.py -q build" before running the tests
+    -d -- run tests in debug mode
+    -L -- run tests in an infinite loop
+    -h -- print this message
+
+The optional modulepath and testcase arguments are regular expressions
+that can be used to limit the number of tests that are run.  The
+modulepath regex must be found in the path of the module that contains
+the tests.  The testcase regex must be found in the name of the test case.
+
+When it finishes, the test harness prints a report of the tests run
+and how long it took.  If errors or failures occured, they will be
+reported along with a traceback.  If one -v is specified, a dot will
+be printed as each test is run.  If two -v's are specified, the name
+of each test will be printed as it runs.
+"""
+
+import os
+import re
+import sys
+import traceback
+import unittest
+
+from distutils.util import get_platform
+
+class ImmediateTestResult(unittest._TextTestResult):
+
+    def _print_traceback(self, msg, err, test, errlist):
+        if self.showAll or self.dots:
+            self.stream.writeln("\n")
+
+        tb = ''.join(traceback.format_exception(*err))
+        self.stream.writeln(msg)
+        self.stream.writeln(tb)
+        errlist.append((test, tb))
+
+    def addError(self, test, err):
+        self._print_traceback("Error in test %s" % test, err,
+                              test, self.errors)
+
+    def addFailure(self, test, err):
+        self._print_traceback("Failure in test %s" % test, err,
+                              test, self.failures)
+
+    def printErrorList(self, flavor, errors):
+        for test, err in errors:
+            self.stream.writeln(self.separator1)
+            self.stream.writeln("%s: %s" % (flavor, self.getDescription(test)))
+            self.stream.writeln(self.separator2)
+            self.stream.writeln(err)
+        
+
+class ImmediateTestRunner(unittest.TextTestRunner):
+
+    def _makeResult(self):
+        return ImmediateTestResult(self.stream, self.descriptions,
+                                   self.verbosity)
+
+# setup list of directories to put on the path
+
+PLAT_SPEC = "%s-%s" % (get_platform(), sys.version[0:3])
+
+def setup_path():
+    DIRS = ["lib",
+            "lib.%s" % PLAT_SPEC,
+            ]
+    for d in DIRS:
+        sys.path.insert(0, d)
+
+
+# Find test files.
+# They live under either a lib.PLAT_SPEC or plain "lib" directory.
+_sep = re.escape(os.sep)
+_pat = "%s(%s|lib)%s" % (_sep, re.escape("lib." + PLAT_SPEC), _sep)
+hasgooddir = re.compile(_pat).search
+del _sep, _pat
+
+class TestFileFinder:
+    def __init__(self):
+        self.files = []
+
+    def visit(self, rx, dir, files):
+        if dir[-5:] != "tests":
+            return
+        # ignore tests that aren't in packages
+        if not "__init__.py" in files:
+            print "not a package", dir
+            return
+        for file in files:
+            if file[:4] == "test" and file[-3:] == ".py":
+                path = os.path.join(dir, file)
+                if not hasgooddir(path):
+                    # built for a different version
+                    continue
+                if rx is not None:
+                    if rx.search(path):
+                        self.files.append(path)
+                else:
+                    self.files.append(path)
+
+def find_tests(filter):
+    if filter is not None:
+        rx = re.compile(filter)
+    else:
+        rx = None
+    finder = TestFileFinder()
+    os.path.walk("build", finder.visit, rx)
+    return finder.files
+
+def package_import(modname):
+    mod = __import__(modname)
+    for part in modname.split(".")[1:]:
+        mod = getattr(mod, part)
+    return mod
+
+def module_from_path(path):
+    """Return the Python package name indiciated by the filesystem path.
+
+    The path starts with build/lib or build /lib.mumble..."""
+
+    assert path[-3:] == '.py'
+    path = path[:-3]
+    dirs = []
+    while path:
+        path, end = os.path.split(path)
+        dirs.insert(0, end)
+    assert dirs[0] == "build"
+    assert dirs[1][:3] == "lib"
+    return ".".join(dirs[2:])
+
+def get_suite(file):
+    assert file[:5] == "build"
+    assert file[-3:] == '.py'
+    modname = module_from_path(file)
+    mod = package_import(modname)
+    try:
+        suite_factory = mod.test_suite
+    except AttributeError, err:
+        return None
+    return suite_factory()
+
+def match(rx, s):
+    if not rx:
+        return 1
+    if rx[0] == '!':
+        return re.search(rx[1:], s) is None
+    else:
+        return re.search(rx, s) is not None
+
+def filter_testcases(s, rx):
+    new = unittest.TestSuite()
+    for test in s._tests:
+        if isinstance(test, unittest.TestCase):
+            name = test.id() # Full test name: package.module.class.method
+            name = name[1 + name.rfind('.'):] # extract method name
+            if match(rx, name):
+                new.addTest(test)
+        else:
+            filtered = filter_testcases(test, rx)
+            if filtered:
+                new.addTest(filtered)
+    return new
+
+def runner(files, test_filter, debug):
+    runner = ImmediateTestRunner(verbosity=VERBOSE)
+    suite = unittest.TestSuite()
+    for file in files:
+        s = get_suite(file)
+        if s is not None:
+            if test_filter is not None:
+                s = filter_testcases(s, test_filter)
+            suite.addTest(s)
+    if debug:
+        suite.debug()
+        return 0
+    r = runner.run(suite)
+    return len(r.errors) + len(r.failures)
+
+def main(module_filter, test_filter):
+    setup_path()
+    files = find_tests(module_filter)
+    files.sort()
+
+    os.chdir("build") 
+
+    if LOOP:
+        while 1:
+            runner(files, test_filter, debug)
+    else:
+        runner(files, test_filter, debug)
+
+if __name__ == "__main__":
+    import getopt
+
+    module_filter = None
+    test_filter = None
+    VERBOSE = 0
+    LOOP = 0
+    debug = 0 # Don't collect test results; simply let tests crash
+    build = 0
+
+    try:
+        opts, args = getopt.getopt(sys.argv[1:], 'vdLbh')
+    except getopt.error, msg:
+        print msg
+        print "Try `python %s -h' for more information." % sys.argv[0]
+        sys.exit(2)
+
+    for k, v in opts:
+        if k == '-v':
+            VERBOSE = VERBOSE + 1
+        elif k == '-d':
+            debug = 1
+        elif k == '-L':
+            LOOP = 1
+        elif k == '-b':
+            build = 1
+        elif k == '-h':
+            print __doc__
+            sys.exit(0)
+
+    if build:
+        cmd = sys.executable + " setup.py -q build"
+        if VERBOSE:
+            print cmd
+        sts = os.system(cmd)
+        if sts:
+            print "Build failed", hex(sts)
+            sys.exit(1)
+
+    if args:
+        if len(args) > 1:
+            test_filter = args[1]
+        module_filter = args[0]
+    try:
+        bad = main(module_filter, test_filter)
+        if bad:
+            sys.exit(1)
+    except ImportError, err:
+        print err
+        print sys.path
+        raise


=== ZEO/CHANGES.txt 1.28 => 1.29 ===
+Revision History, Zope Enterprise Objects, version 2
 
-  ZEO 1.0 final
+  ZEO 2.0 alpha 1
 
-    Bugs fixed
-
-    - Fixed a bug that prevented a client from reconnecting to a
-      server if the server restarted.
-
-    - Fixed start.py so that it prints a message on the console when
-      it fails in addition to using zLOG.
-
-    - Fleshed out installation instructions and version dependencies.
-
-  ZEO 1.0 beta 3
-
-    Bugs fixed
-
-     - The previous beta failed to startup a connection to a server
-       running any storage that did not support transactionalUndo.
-       The server now checks for supported features in a way that will
-       not cause errors.
-
-  ZEO 1.0 beta 2
-
-    New Features
-
-     - Support for transactionalUndo when the underlying storage
-       supports it.
-
-     - A unit test suite was added.  The test suite requires that
-       PyUnit be installed; it's part of the standard library in
-       Python 2.1.  It also requires that the ZODB installation
-       defines the ZODB.tests package.  If these conditions don't
-       hold, the test suite can't be run.
-
-    Bugs fixed
-
-     - A cache invalidation bug was fixed for commitVersion and
-       abortVersion.  It was possible for a load the occurred between
-       a commit version and a tpc_finish to store invalid data in the
-       cache. 
-
-     - The undoInfo() method defines default values for its arguments.
-
-     - The final argument to tpc_begin(), the transaction extended
-       metadata, was ignored.
-
-     - A theoretical bug in the client caching code for objects
-       created in versions was fixed.
-
-  ZEO 1.0 beta 1
-
-    New Features
-
-     - Improved release organization.
-
-     - Moved documentation and misc files out of the ZEO package into
-       the release directory.
-
-    Bugs fixed
-
-     - Normal shutdown was reported as a panic.
-
-     - The signal exception handler was disabled.
-
-     - Errors arising from incompatable versions of cPickle were
-       uclear.
-
-
-  ZEO 0.5.0
-
-    New Features
-
-      - The server can be made to reopen it's log file
-        by sending it a HUP (on systems supporting signals).  Note
-        that this requires a change to asyncore to catch interrupted
-        system calls on some platforms.
-
-      - The shutdown signals have been changed:
-
-        o To shutdown, use TERM 
-
-        o To restart, use INT.  (This must be send to the 
-          child, not the parent.
-
-      - Client scripts can now be written to pack a remote storage and
-        wait for the pack results.  This is handy when packing as part
-        of cron jobs.
-
-      - It is no longer necessary to symbolically link cPickle or
-        ZServer. ZServer is no longer necessary at all.
-
-      - A Zope-style INSTANCE_HOME and var directory are no longer 
-        needed.
-
-      - An option, -d, was added to facilitate generation of a
-        detailed debug log while running in the background.
-
-      - The documentation has been simplified and spread over multiple
-        files in the doc subdirectory.
-
-    Bugs Fixed
-
-      - Application-level conflict resolution, introduced in Zope
-        2.3.1, was not supported. This caused the ZEO cache to be
-        written incorrectly.
-
-      - A possible (but unobserved) race condition that could
-        lead to ZEO cache corruption was corrected.
-
-      - ZEO clients could fail to start if they needed data that
-        wasn't in their cache and if they couldn't talk to a ZEO
-        server right away. For now, on startup, the client storage
-        will wait to connect to a storage before returning from
-        initialization. 
-
-      - Restarting the ZEO server shortly after shutting down could
-        lead to "address already in use" errors.
-
-      - User-level eceptions, like undo, version-lock, and conflict
-        errors were logged in the server event log.
-
-      - Pack errors weren't logged in the server event log.
-
-      - If an attempt was made to commit a transaction with updates
-        while the client storage was disconnected from the server, 
-        no further write transactions would be allowed, even after 
-	reconnection, and the site would eventually hang.
-
-      - A forgotten argument made it unreliable to start a ClientStorage
-        after the main loop has started.
-
-      - In combination with recent changes in zdeamon, startup errors
-        could cause infinite loops.
-
-      - The handling of the Python global, __debug__, was not
-        compatible with Python 2.1.
-
-      - If an exception raised on the server which could not be
-        unpickled on the client could cause the client connection to
-        fail. 
-
-  Planned for (future) ZEO releases
-
-    New Features
-
-      - Provide optional data compression. This should enhance
-        performance over slow connections to the storage server and
-        reduce the server I/O load.
-
-      - Provide optional authentication adapters that allow for
-        pluggable authentication and encryption schemes.
-
-        This is a feature that is listed on the ZEO fact sheet, but
-        that didn't make it into the 1.0 release. Firewall or secure
-        tunneling techniques can be used to secure communication
-        between clients and the storage for now when the client and
-        storage are on different machines. (If they are on the same
-        machine, then unix-domain sockets or the loop-back interface
-        can be used.)
-
-      - Provide an option to start a client process without waiting
-        for a connection to the storage server.  This was the original
-        intent, however, it turns out that it can be extremely
-        problemantic to get storage errors resulting from attempts to
-        read objects not in the cache during process (e.g. Zope)
-        startup. In addition, some smarter cache management can be
-        done to decrease the probability of important objects being
-        removed from the cache.  
- 
-      - Provide improved client cache management. This will involve
-        changes like:
-
-        o Increasing the number of cache files to reduce the number of
-          objects lost from the cache (or that need to be recovered)
-          when the cache "rolls over".
-
-        o Use separate indexes for each cache.  
-
-        o use better cache indexing structures
-
-  ZEO 0.4.1
-
-    Bugs fixed
-
-      - Improperly handled server exeptions could cause clients to
-        lock up.
-
-      - Misshandling of client transaction meta data could cause
-        server errors because transaction ids were mangled.
-
-      - The storage server didn't close sockets on shutdown. This
-        could sometimes make it necessary to wait before restarting
-        the server to avoid "address already in use" messages.
-
-      - The storage server did not log shutdown.
-
-  ZEO 0.4
-
-    Bugs fixed
-
-      - The new (in 0.3) logic to switch to an ordinary user when
-        started as root was executed too late so that some files were
-        incorrectly owned by root. This caused ZEO clients to fail
-        when the cache files were rotated.
-
-      - There were some unusual error conditions that were not handled
-        correctly that could cause clients to fail.  This was detected
-        only when ZEO was put into production on zope.org.
-
-      - The cache files weren't rotated on reads. This could cause the
-        caches to grow way beyond their target sizes.
-
-      - Exceptions raised in the servers asynchronous store handler
-        could cause the client and server to get out of sync.
-
-      - Connection and disconnection events weren't logged on the
-        server.
-
-    Features added
-
-      - ClientStorage objects have two new constructor arguments,
-        min_disconnect_poll and max_disconnect_poll to set the minimum
-        and maximum times to wait, in seconds, before retrying to
-        reconnect when disconnected from the ZEO server.
-
-      - A call to get database info on startup was eliminated in
-        favor of having the server send the information
-        automatically. This eliminates a round-trip and, therefore
-        speeds up startup a tiny bit.
-
-      - Database size info is now sent to all clients (asynchronously)
-        after a pack and after a transaction commit, allowing all
-        clients to have timely size information.
-
-      - Added client logging of connection attempts.
-
-      - Added a misc subdirectory with sample storage server start and
-        stop scripts and with a sample custom_zodb.py module.
-
-  ZEO 0.3.0
-
-    Bugs fixed
-
-      - Large transactions (e.g. ZCatalog updates) could cause
-        spurious conflict errors that could, eventually, make it
-        impossible to modify some objects without restarting Zope.
-
-      - Temporary non-persistent cache files were not removed at the
-        end of a run.
-
-    Features added
-
-      - On Unix, when the storage server start script is run as root,
-        the script will switch to a different user (nobody by
-        default). There is a new '-u' option that can be used to
-        specify the user.
-
-      - On Unix the server will gracefully close served storages when
-        the server is killed with a SIGTERM or SIGHUP. If a
-        FileStorage is being served, then an index file will be
-        written.
-
-  ZEO 0.2.3
-
-    Bugs fixed
-
-      - Versions didn't work. Not even close. :|
- 
-      - If a client was disconnected from a server during transaction
-        commit, then, when the client was reconnected to the server,
-        attempts to commit transactions caused the client to hang.
-
-      - The server would fail (and successfully automatically restart)
-        if an unpickleable exception was raised.
-
-  ZEO 0.2.2
-
-    Bugs fixed
-
-      - The storage server didn't fully implement a new ZODB storage
-        protocol. This caused serving of FileStorages to fail in Zope
-        2.2.1, since FileStorages now use this protocol.
-
-      - In the start.py start script
-
-        o The '-S' option did not allow spaces between the option and it's
-          argument.  
-
-        o The '-S' option did not work with FileStorages.
-
-        o The README file didn't mention the '-S' option.
-
-  ZEO 0.2.1
-
-    Bugs fixed
-
-      - ZEO clients didn't work properly (effectively at all) on
-        Solaris or Windows NT.
-
-      - An error in the handling of the distributed transaction lock
-        could cause a client to stop writing and eventually hang if
-        two clients tried to commit a transaction at the same time.
-
-      - Extra (harmless) messages were sent from the server 
-        when invalidating objects during a commit.
-
-      - New protocols (especially 'loadSerial'), used for looking at 
-        DTML historical versions, were not implemented.
-
-    Features
-
-      - The '-S' option was added to the storage server startup script
-        to allow selection of one or more storages to serve.     
-
-  ZEO 0.2 
-
-    This release is expected to be close to beta quality. Initially, the
-    primary goals of this release were to:
-
-      - Correct some consistency problems that had been observed in
-        0.1 on starup.
-
-      - Allow ZEO clients to detect, survive, and recover from
-        disconnection from the ZEO server.
-
-    Based on some feedback from some folks who tried 0.1, improving
-    write performance was made a priority.
-
-    Features
-
-      - The ZEO Client now handles server failures gracefully:
-
-        o The client with a persistent cache can generally startup
-	  even if the server is not running, assuming that it has at
-	  least a minimal number of objects in the cache.
-
-        o The client will continue to function even if the server
-          connection is interuppted.
-
-        o Server availability is detected by the client (which tries
-          to connect to the server every few minutes). A disconnected
-          client will automatically reconnect to an available server.
-
-        o When the client is disconnected, write transactions cannot
-          be performed.  Reads fail for objects that are not in the
-          cache.
-
-      - Performance enhancements
-
-        The speed of write-intensive operations have been improved
-	approximately 70%.  When using Unix domain sockets for
-	client/server communication, ZEO transactions take roughly 2-3
-	times as long as FileStorage transactions to commit.
-	(This was based on some tests. Your mileage may vary.)
-
-      - Packing support was added. Note that packing is done
-        asynchrounously. The client returns immediately from a pack
-        call. The server packs in a thread and sends updated
-        statistics to the client when packing is completed.
-
-      - Support for Unix-domain sockets was added.
-
-      - Pickles sent to the server are now checked to make sure that
-        they don't contain unapproved instance or global-variable
-        (function) pickles.
-
-    Bugs fixed
-
-      - Data could be badly inconsistent when a persistent cache
-        was started, due to a bug in the cache initialization logic.
+    Brief overview of the differences between ZEO 1.0 and 2.0.
       
-      - The application was allowed to begin operation while the cache
-        was being verified. This could lead to harmful inconsistencies.
-
-    Changes made to Zope to support ZEO
-
-      - A number of changes were made to ZODB to support asynchronous
-        storage during transaction commit.
-
-      - Normally Zope updates the database during startup to reflect
-        product changes. This behavior is now suppressed when the
-        ZEO_CLIENT environment variable is set. It doesn't make sense
-        for many clients to update the database for the same products.
+      - New protocol.
 
-      - The asyncore module was modified to add support for multiple
-        asyncore loops. This change was applied to asyncore in the
-        Zope and the (official, owned by Sam Rushing) medusa CVS
-        trees.
+        ZEO 2 uses a different wire protocol and a different API to
+        make RPC calls.  The new protocol was designed to be flexible
+        and simple.  It includes an initial handshake to set the 
+        version number, which should allow future changes to the
+        protocol while reducing the difficulty of upgrades.
+
+      - Better handling of concurrent commits.
+
+        The ZEO server serializes concurrent commits to guarantee
+        consistency; the mechanism is often called the distributed
+        commit lock.  ZEO 2 improves the efficiency of concurrent
+        commits by allowing data to be transferred to the server
+        before entering the commit lock.
 
-      - A new module, ThreadedAsync.py has been added in the Zope
-        lib/python directory.  This module provides notification to
-        async objects (like ZEO clients) to let them know when the
-        asyncore main loop has started. This was needed to enable use
-        of async code before the main loop starts.
+      - The ZEO client and server can be configured to operate in
+        read-only mode.
 
-  ZEO 0.1 (aka "iteration 1")
+      - A ZEO client can be configured with multiple server addresses.
+        It uses the first server it can connect to.
 
-    This was an initial alpha of ZEO that demonstrated basic
-    functionalities. It lacked robustness and has some performance
-    problems on writes.
+      - The wait_for_server_on_startup keyword argument to
+        ClientStorage has been renamed wait.