[Zodb-checkins] CVS: ZODB3/Tools - zeoserverlog.py:1.1.2.1 zodbload.py:1.1.2.1 README.txt:1.1.2.2

Tim Peters tim.one at comcast.net
Wed Aug 27 17:09:21 EDT 2003


Update of /cvs-repository/ZODB3/Tools
In directory cvs.zope.org:/tmp/cvs-serv20852/Tools

Modified Files:
      Tag: ZODB3-3_1-branch
	README.txt 
Added Files:
      Tag: ZODB3-3_1-branch
	zeoserverlog.py zodbload.py 
Log Message:
New scripts from Jim (Fulton), to provoke heavy loads and produce stats
about ZEO performance.


=== Added File ZODB3/Tools/zeoserverlog.py ===
##############################################################################
#
# Copyright (c) 2003 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.
#
##############################################################################
"""Tools for analyzing ZEO Server logs.

This script contains a number of commands, implemented by command
functions. To run a command, give the command name and it's arguments
as arguments to this script.

Commands:

  blocked_times file threshold

     Output a summary of episodes where thransactions were blocked
     when the episode lasted at least threshold seconds.

     The file may be a file name or - to read from standard input.
     The file may also be a command:

       script blocked_times 'bunzip2 <foo.log.bz2' 60

     If the file is a command, it must contain at least a single
     space.

     The columns of output are:

     - The time the episode started

     - The seconds from the start of the episode until the blocking
       transaction finished.

     - The client id (host and port) of the blocking transaction.

     - The seconds from the start of the episode until the end of the
       episode.

  time_calls file threshold

     Time how long calls took. Note that this is normally combined
     with grep to time just a particulat kind of call:

       script time_calls 'bunzip2 <foo.log.bz2 | grep tpc_finish' 10

       time_trans threshold

     The columns of output are:

     - The time of the call invocation

     - The seconds from the call to the return

     - The client that made the call.

  time_trans file threshold

    Output a summary of transactions that held the global transaction
    lock for at least threshold seconds. (This is the time from when
    voting starts until the transaction is completed by the server.)

    The columns of output are:

    - time that the vote started.

    - client id

    - number of objects written / number of objects updated

    - seconds from tpc_begin to vote start

    - seconds spent voting

    - vote status: n=normal, d=delayed, e=error

    - seconds wating between vote return and finish call

    - time spent finishing or 'abort' if the transaction aborted

  minute file

    Compute production statistics by minute

    The columns of output are:

    - date/time

    - Number of active clients

    - number of reads

    - number of stores

    - number of commits (finish)

    - number of aborts

    - number of transactions (commits + aborts)

    Summary statistics are printed at the end

  minutes file

    Show just the summary statistics for production by minute.

  hour file

    Compute production statistics by hour

  hours file

    Show just the summary statistics for production by hour.

  day file

    Compute production statistics by day

  days file

    Show just the summary statistics for production by day.


$Id: zeoserverlog.py,v 1.1.2.1 2003/08/27 20:09:20 tim_one Exp $
"""

import datetime, sys, re, os


def time(line):
    d = line[:10]
    t = line[11:19]
    y, mo, d = map(int, d.split('-'))
    h, mi, s = map(int, t.split(':'))
    return datetime.datetime(y, mo, d, h, mi, s)


def sub(t1, t2):
    delta = t2 - t1
    return delta.days*86400.0+delta.seconds+delta.microseconds/1000000.0



waitre = re.compile(r'Clients waiting: (\d+)')
idre = re.compile(r' ZSS:\d+/(\d+.\d+.\d+.\d+:\d+) ')
def blocked_times(args):
    f, thresh = args

    t1 = t2 = cid = blocking = waiting = 0
    last_blocking = False

    thresh = int(thresh)

    for line in xopen(f):
        line = line.strip()

        if line.endswith('Blocked transaction restarted.'):
            blocking = False
            waiting = 0
        else:
            s = waitre.search(line)
            if not s:
                continue
            waiting = int(s.group(1))
            blocking = line.find(
                'Transaction blocked waiting for storage') >= 0

        if blocking and waiting == 1:
            t1 = time(line)
            t2 = t1

        if not blocking and last_blocking:
            last_wait = 0
            t2 = time(line)
            cid = idre.search(line).group(1)

        if waiting == 0:
            d = sub(t1, time(line))
            if d >= thresh:
                print t1, sub(t1, t2), cid, d
            t1 = t2 = cid = blocking = waiting = last_wait = max_wait = 0

        last_blocking = blocking

connidre = re.compile(r' zrpc-conn:(\d+.\d+.\d+.\d+:\d+) ')
def time_calls(f):
    f, thresh = f
    if f == '-':
        f = sys.stdin
    else:
        f = xopen(f)

    thresh = float(thresh)
    t1 = None
    maxd = 0

    for line in f:
        line = line.strip()

        if ' calling ' in line:
            t1 = time(line)
        elif ' returns ' in line and t1 is not None:
            d = sub(t1, time(line))
            if d >= thresh:
                print t1, d, connidre.search(line).group(1)
            maxd = max(maxd, d)
            t1 = None

    print maxd

def xopen(f):
    if f == '-':
        return sys.stdin
    if ' ' in f:
        return os.popen(f, 'r')
    return open(f)

def time_tpc(f):
    f, thresh = f
    if f == '-':
        f = sys.stdin
    else:
        f = xopen(f)

    thresh = float(thresh)
    transactions = {}

    for line in f:
        line = line.strip()

        if ' calling vote(' in line:
            cid = connidre.search(line).group(1)
            transactions[cid] = time(line),
        elif ' vote returns None' in line:
            cid = connidre.search(line).group(1)
            transactions[cid] += time(line), 'n'
        elif ' vote() raised' in line:
            cid = connidre.search(line).group(1)
            transactions[cid] += time(line), 'e'
        elif ' vote returns ' in line:
            # delayed, skip
            cid = connidre.search(line).group(1)
            transactions[cid] += time(line), 'd'
        elif ' calling tpc_abort(' in line:
            cid = connidre.search(line).group(1)
            if cid in transactions:
                t1, t2, vs = transactions[cid]
                t = time(line)
                d = sub(t1, t)
                if d >= thresh:
                    print 'a', t1, cid, sub(t1, t2), vs, sub(t2, t)
                del transactions[cid]
        elif ' calling tpc_finish(' in line:
            if cid in transactions:
                cid = connidre.search(line).group(1)
                transactions[cid] += time(line),
        elif ' tpc_finish returns ' in line:
            if cid in transactions:
                t1, t2, vs, t3 = transactions[cid]
                t = time(line)
                d = sub(t1, t)
                if d >= thresh:
                    print 'c', t1, cid, sub(t1, t2), vs, sub(t2, t3), sub(t3, t)
                del transactions[cid]


newobre = re.compile(r"storea\(.*, '\\x00\\x00\\x00\\x00\\x00")
def time_trans(f):
    f, thresh = f
    if f == '-':
        f = sys.stdin
    else:
        f = xopen(f)

    thresh = float(thresh)
    transactions = {}

    for line in f:
        line = line.strip()

        if ' calling tpc_begin(' in line:
            cid = connidre.search(line).group(1)
            transactions[cid] = time(line), [0, 0]
        if ' calling storea(' in line:
            cid = connidre.search(line).group(1)
            if cid in transactions:
                transactions[cid][1][0] += 1
                if not newobre.search(line):
                    transactions[cid][1][1] += 1

        elif ' calling vote(' in line:
            cid = connidre.search(line).group(1)
            if cid in transactions:
                transactions[cid] += time(line),
        elif ' vote returns None' in line:
            cid = connidre.search(line).group(1)
            if cid in transactions:
                transactions[cid] += time(line), 'n'
        elif ' vote() raised' in line:
            cid = connidre.search(line).group(1)
            if cid in transactions:
                transactions[cid] += time(line), 'e'
        elif ' vote returns ' in line:
            # delayed, skip
            cid = connidre.search(line).group(1)
            if cid in transactions:
                transactions[cid] += time(line), 'd'
        elif ' calling tpc_abort(' in line:
            cid = connidre.search(line).group(1)
            if cid in transactions:
                try:
                    t0, (stores, old), t1, t2, vs = transactions[cid]
                except ValueError:
                    pass
                else:
                    t = time(line)
                    d = sub(t1, t)
                    if d >= thresh:
                        print t1, cid, "%s/%s" % (stores, old), \
                              sub(t0, t1), sub(t1, t2), vs, \
                              sub(t2, t), 'abort'
                del transactions[cid]
        elif ' calling tpc_finish(' in line:
            if cid in transactions:
                cid = connidre.search(line).group(1)
                transactions[cid] += time(line),
        elif ' tpc_finish returns ' in line:
            if cid in transactions:
                t0, (stores, old), t1, t2, vs, t3 = transactions[cid]
                t = time(line)
                d = sub(t1, t)
                if d >= thresh:
                    print t1, cid, "%s/%s" % (stores, old), \
                          sub(t0, t1), sub(t1, t2), vs, \
                          sub(t2, t3), sub(t3, t)
                del transactions[cid]

def minute(f, slice=16, detail=1, summary=1):
    f, = f

    if f == '-':
        f = sys.stdin
    else:
        f = xopen(f)

    mlast = r = s = c = a = cl = None
    rs = []
    ss = []
    cs = []
    as = []
    ts = []
    cls = []

    for line in f:
        line = line.strip()
        if (line.find('returns') > 0
            or line.find('storea') > 0
            or line.find('tpc_abort') > 0
            ):
            client = connidre.search(line).group(1)
            m = line[:slice]
            if m != mlast:
                if mlast:
                    if detail:
                        print mlast, len(cl), r, s, c, a, a+c
                    cls.append(len(cl))
                    rs.append(r)
                    ss.append(s)
                    cs.append(c)
                    as.append(a)
                    ts.append(c+a)
                mlast = m
                r = s = c = a = 0
                cl = {}
            if line.find('zeoLoad') > 0:
                r += 1
                cl[client] = 1
            elif line.find('storea') > 0:
                s += 1
                cl[client] = 1
            elif line.find('tpc_finish') > 0:
                c += 1
                cl[client] = 1
            elif line.find('tpc_abort') > 0:
                a += 1
                cl[client] = 1

    if mlast:
        if detail:
            print mlast, len(cl), r, s, c, a, a+c
        cls.append(len(cl))
        rs.append(r)
        ss.append(s)
        cs.append(c)
        as.append(a)
        ts.append(c+a)

    if summary:
        print
        print 'Summary:     \t', '\t'.join(('min', '10%', '25%', 'med',
                                            '75%', '90%', 'max', 'mean'))
        print "n=%6d\t" % len(cls), '-'*62
        print 'Clients: \t', '\t'.join(map(str,stats(cls)))
        print 'Reads:   \t', '\t'.join(map(str,stats( rs)))
        print 'Stores:  \t', '\t'.join(map(str,stats( ss)))
        print 'Commits: \t', '\t'.join(map(str,stats( cs)))
        print 'Aborts:  \t', '\t'.join(map(str,stats( as)))
        print 'Trans:   \t', '\t'.join(map(str,stats( ts)))

def stats(s):
    s.sort()
    min = s[0]
    max = s[-1]
    n = len(s)
    out = [min]
    ni = n + 1
    for p in .1, .25, .5, .75, .90:
        lp = ni*p
        l = int(lp)
        if lp < 1 or lp > n:
            out.append('-')
        elif abs(lp-l) < .00001:
            out.append(s[l-1])
        else:
            out.append(int(s[l-1] + (lp - l) * (s[l] - s[l-1])))

    mean = 0.0
    for v in s:
        mean += v

    out.extend([max, int(mean/n)])

    return out

def minutes(f):
    minute(f, 16, detail=0)

def hour(f):
    minute(f, 13)

def day(f):
    minute(f, 10)

def hours(f):
    minute(f, 13, detail=0)

def days(f):
    minute(f, 10, detail=0)


new_connection_idre = re.compile(r"new connection \('(\d+.\d+.\d+.\d+)', (\d+)\):")
def verify(f):
    f, = f

    if f == '-':
        f = sys.stdin
    else:
        f = xopen(f)

    t1 = None
    nv = {}
    for line in f:
        if line.find('new connection') > 0:
            m = new_connection_idre.search(line)
            cid = "%s:%s" % (m.group(1), m.group(2))
            nv[cid] = [time(line), 0]
        elif line.find('calling zeoVerify(') > 0:
            cid = connidre.search(line).group(1)
            nv[cid][1] += 1
        elif line.find('calling endZeoVerify()') > 0:
            cid = connidre.search(line).group(1)
            t1, n = nv[cid]
            if n:
                d = sub(t1, time(line))
                print cid, t1, n, d, n and (d*1000.0/n) or '-'


if __name__ == '__main__':
    globals()[sys.argv[1]](sys.argv[2:])


=== Added File ZODB3/Tools/zodbload.py ===
##############################################################################
#
# Copyright (c) 2003 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 script for testing ZODB under a heavy zope-like load.

Note that, to be as realistic as possible with ZEO, you should run this
script multiple times, to simulate multiple clients.

Here's how this works.

The script starts some number of threads.  Each thread, sequentially
executes jobs.  There is a job producer that produces jobs.

Input data are provided by a mail producer that hands out message from
a mailbox.

Execution continues until there is an error, which will normally occur
when the mailbox is exhausted.

Command-line options are used to provide job definitions. Job
definitions have perameters of the form name=value.  Jobs have 2
standard parameters:

  frequency=integer

     The frequency of the job. The default is 1.

  sleep=float

     The number os seconds to sleep before performing the job. The
     default is 0.

Usage: loadmail2 [options]

  Options:

    -edit [frequency=integer] [sleep=float]

       Define an edit job. An edit job edits a random already-saved
       email message, deleting and inserting a random number of words.

       After editing the message, the message is (re)cataloged.

    -insert [number=int] [frequency=integer] [sleep=float]

       Insert some number of email messages.

    -index [number=int] [frequency=integer] [sleep=float]

       Insert and index (catalog) some number of email messages.

    -search [terms='word1 word2 ...'] [frequency=integer] [sleep=float]

       Search the catalog. A query is givem with one or more terms as
       would be entered into a typical seach box.  If no query is
       given, then queries will be randomly selected based on a set of
       built-in word list.

    -setup

       Set up the database. This will delete any existing Data.fs
       file.  (Of course, this may have no effect, if there is a
       custom_zodb that defined a different storage.) It also adds a
       mail folder and a catalog.

    -options file

       Read options from the given file. Th efile should be a python
       source file that defines a sequence of options named 'options'.

    -threads n

       Specify the number of threads to execute. If not specified (< 2),
       then jobs are run in a single (main) thread.

    -mbox filename

       Specify the mailbox for getting input data.

$Id: zodbload.py,v 1.1.2.1 2003/08/27 20:09:20 tim_one Exp $
"""

import mailbox
import math
import os
import random
import re
import sys
import threading
import time

class JobProducer:

    def __init__(self):
        self.jobs = []

    def add(self, callable, frequency, sleep, repeatp=0):
        self.jobs.extend([(callable, sleep, repeatp)] * int(frequency))
        random.shuffle(self.jobs)

    def next(self):
        factory, sleep, repeatp = random.choice(self.jobs)
        time.sleep(sleep)
        callable, args = factory.create()
        return factory, callable, args, repeatp

    def __nonzero__(self):
        return not not self.jobs



class MBox:

    def __init__(self, filename):
        if ' ' in filename:
            filename, min, max = filename.split()
            min = int(min)
            max = int(max)
        else:
            min = max = 0

        if filename.endswith('.bz2'):
            f = os.popen("bunzip2 <"+filename, 'r')
            filename = filename[-4:]
        else:
            f = open(filename)

        self._mbox = mb = mailbox.UnixMailbox(f)

        self.number = min
        while min:
            mb.next()
            min -= 1

        self._lock = threading.Lock()
        self.__name__ = os.path.splitext(os.path.split(filename)[1])[0]
        self._max = max

    def next(self):
        self._lock.acquire()
        try:
            if self._max > 0 and self.number >= self._max:
                raise IndexError(self.number + 1)
            message = self._mbox.next()
            message.body = message.fp.read()
            message.headers = list(message.headers)
            self.number += 1
            message.number = self.number
            message.mbox = self.__name__
            return message
        finally:
            self._lock.release()

bins = 9973
#bins = 11
def mailfolder(app, mboxname, number):
    mail = getattr(app, mboxname, None)
    if mail is None:
        app.manage_addFolder(mboxname)
        mail = getattr(app, mboxname)
        from BTrees.Length import Length
        mail.length = Length()
        for i in range(bins):
            mail.manage_addFolder('b'+str(i))
    bin = hash(str(number))%bins
    return getattr(mail, 'b'+str(bin))


def VmSize():

    try:
        f = open('/proc/%s/status' % os.getpid())
    except:
        return 0
    else:
        l = filter(lambda l: l[:7] == 'VmSize:', f.readlines())
        if l:
            l = l[0][7:].strip().split()[0]
            return int(l)
    return 0

def setup(lib_python):
    try:
        os.remove(os.path.join(lib_python, '..', '..', 'var', 'Data.fs'))
    except:
        pass
    import Zope
    import Products
    import AccessControl.SecurityManagement
    app=Zope.app()

    Products.ZCatalog.ZCatalog.manage_addZCatalog(app, 'cat', '')

    from Products.ZCTextIndex.ZCTextIndex import PLexicon
    from Products.ZCTextIndex.Lexicon import Splitter, CaseNormalizer

    app.cat._setObject('lex',
                       PLexicon('lex', '', Splitter(), CaseNormalizer())
                       )

    class extra:
        doc_attr = 'PrincipiaSearchSource'
        lexicon_id = 'lex'
        index_type = 'Okapi BM25 Rank'

    app.cat.addIndex('PrincipiaSearchSource', 'ZCTextIndex', extra)

    get_transaction().commit()

    system = AccessControl.SpecialUsers.system
    AccessControl.SecurityManagement.newSecurityManager(None, system)

    app._p_jar.close()

def do(db, f, args):
    """Do something in a transaction, retrying of necessary

    Measure the speed of both the compurartion and the commit
    """
    from ZODB.POSException import ConflictError
    wcomp = ccomp = wcommit = ccommit = 0.0
    rconflicts = wconflicts = 0
    start = time.time()

    while 1:
        connection = db.open()
        try:
            get_transaction().begin()
            t=time.time()
            c=time.clock()
            try:
                try:
                    r = f(connection, *args)
                except ConflictError:
                    rconflicts += 1
                    get_transaction().abort()
                    continue
            finally:
                wcomp += time.time() - t
                ccomp += time.clock() - c

            t=time.time()
            c=time.clock()
            try:
                try:
                    get_transaction().commit()
                    break
                except ConflictError:
                    wconflicts += 1
                    get_transaction().abort()
                    continue
            finally:
                wcommit += time.time() - t
                ccommit += time.clock() - c
        finally:
            connection.close()

    return start, wcomp, ccomp, rconflicts, wconflicts, wcommit, ccommit, r

def run1(tid, db, factory, job, args):
    (start, wcomp, ccomp, rconflicts, wconflicts, wcommit, ccommit, r
     ) = do(db, job, args)
    start = "%.4d-%.2d-%.2d %.2d:%.2d:%.2d" % time.localtime(start)[:6]
    print "%s %s %8.3g %8.3g %s %s\t%8.3g %8.3g %s %r" % (
        start, tid, wcomp, ccomp, rconflicts, wconflicts, wcommit, ccommit,
        factory.__name__, r)

def run(jobs, tid=''):
    import Zope
    while 1:
        factory, job, args, repeatp = jobs.next()
        run1(tid, Zope.DB, factory, job, args)
        if repeatp:
            while 1:
                i = random.randint(0,100)
                if i > repeatp:
                    break
                run1(tid, Zope.DB, factory, job, args)


def index(connection, messages, catalog):
    app = connection.root()['Application']
    for message in messages:
        mail = mailfolder(app, message.mbox, message.number)
        docid = 'm'+str(message.number)
        mail.manage_addDTMLDocument(docid, file=message.body)

        # increment counted
        getattr(app, message.mbox).length.change(1)

        doc = mail[docid]
        for h in message.headers:
            h = h.strip()
            l = h.find(':')
            if l <= 0:
                continue
            name = h[:l].lower()
            if name=='subject':
                name='title'
            v = h[l+1:].strip()
            type='string'

            if name=='title':
                doc.manage_changeProperties(title=h)
            else:
                try:
                    doc.manage_addProperty(name, v, type)
                except:
                    pass
        if catalog:
            app.cat.catalog_object(doc)

    return message.number

class IndexJob:
    needs_mbox = 1
    catalog = 1
    prefix = 'index'

    def __init__(self, mbox, number=1):
        self.__name__ = "%s%s_%s" % (self.prefix, number, mbox.__name__)
        self.mbox, self.number = mbox, int(number)

    def create(self):
        messages = [self.mbox.next() for i in range(self.number)]
        return index, (messages, self.catalog)


class InsertJob(IndexJob):
    catalog = 0
    prefix = 'insert'

wordre = re.compile(r'(\w{3,20})')
stop = 'and', 'not'
def edit(connection, mbox, catalog=1):
    app = connection.root()['Application']
    mail = getattr(app, mbox.__name__, None)
    if mail is None:
        time.sleep(1)
        return "No mailbox %s" % mbox.__name__

    nmessages = mail.length()
    if nmessages < 2:
        time.sleep(1)
        return "No messages to edit in %s" % mbox.__name__

    # find a message to edit:
    while 1:
        number = random.randint(1, nmessages-1)
        did = 'm' + str(number)

        mail = mailfolder(app, mbox.__name__, number)
        doc = getattr(mail, did, None)
        if doc is not None:
            break

    text = doc.raw.split()
    norig = len(text)
    if norig > 10:
        ndel = int(math.exp(random.randint(0, int(math.log(norig)))))
        nins = int(math.exp(random.randint(0, int(math.log(norig)))))
    else:
        ndel = 0
        nins = 10

    for j in range(ndel):
        j = random.randint(0,len(text)-1)
        word = text[j]
        m = wordre.search(word)
        if m:
            word = m.group(1).lower()
            if (not wordsd.has_key(word)) and word not in stop:
                words.append(word)
                wordsd[word] = 1
        del text[j]

    for j in range(nins):
        word = random.choice(words)
        text.append(word)

    doc.raw = ' '.join(text)

    if catalog:
        app.cat.catalog_object(doc)

    return norig, ndel, nins

class EditJob:
    needs_mbox = 1
    prefix = 'edit'
    catalog = 1

    def __init__(self, mbox):
        self.__name__ = "%s_%s" % (self.prefix, mbox.__name__)
        self.mbox = mbox

    def create(self):
        return edit, (self.mbox, self.catalog)

class ModifyJob(EditJob):
    prefix = 'modify'
    catalog = 0


def search(connection, terms, number):
    app = connection.root()['Application']
    cat = app.cat
    n = 0

    for i in number:
        term = random.choice(terms)

        results = cat(PrincipiaSearchSource=term)
        n += len(results)
        for result in results:
            did = result.getObject().getId()

    return n

class SearchJob:

    def __init__(self, terms='', number=10):

        if terms:
            terms = terms.split()
            self.__name__ = "search_" + '_'.join(terms)
            self.terms = terms
        else:
            self.__name__ = 'search'
            self.terms = words

        number = min(int(number), len(self.terms))
        self.number = range(number)

    def create(self):
        return search, (self.terms, self.number)


words=['banishment', 'indirectly', 'imprecise', 'peeks',
'opportunely', 'bribe', 'sufficiently', 'Occidentalized', 'elapsing',
'fermenting', 'listen', 'orphanage', 'younger', 'draperies', 'Ida',
'cuttlefish', 'mastermind', 'Michaels', 'populations', 'lent',
'cater', 'attentional', 'hastiness', 'dragnet', 'mangling',
'scabbards', 'princely', 'star', 'repeat', 'deviation', 'agers',
'fix', 'digital', 'ambitious', 'transit', 'jeeps', 'lighted',
'Prussianizations', 'Kickapoo', 'virtual', 'Andrew', 'generally',
'boatsman', 'amounts', 'promulgation', 'Malay', 'savaging',
'courtesan', 'nursed', 'hungered', 'shiningly', 'ship', 'presides',
'Parke', 'moderns', 'Jonas', 'unenlightening', 'dearth', 'deer',
'domesticates', 'recognize', 'gong', 'penetrating', 'dependents',
'unusually', 'complications', 'Dennis', 'imbalances', 'nightgown',
'attached', 'testaments', 'congresswoman', 'circuits', 'bumpers',
'braver', 'Boreas', 'hauled', 'Howe', 'seethed', 'cult', 'numismatic',
'vitality', 'differences', 'collapsed', 'Sandburg', 'inches', 'head',
'rhythmic', 'opponent', 'blanketer', 'attorneys', 'hen', 'spies',
'indispensably', 'clinical', 'redirection', 'submit', 'catalysts',
'councilwoman', 'kills', 'topologies', 'noxious', 'exactions',
'dashers', 'balanced', 'slider', 'cancerous', 'bathtubs', 'legged',
'respectably', 'crochets', 'absenteeism', 'arcsine', 'facility',
'cleaners', 'bobwhite', 'Hawkins', 'stockade', 'provisional',
'tenants', 'forearms', 'Knowlton', 'commit', 'scornful',
'pediatrician', 'greets', 'clenches', 'trowels', 'accepts',
'Carboloy', 'Glenn', 'Leigh', 'enroll', 'Madison', 'Macon', 'oiling',
'entertainingly', 'super', 'propositional', 'pliers', 'beneficiary',
'hospitable', 'emigration', 'sift', 'sensor', 'reserved',
'colonization', 'shrilled', 'momentously', 'stevedore', 'Shanghaiing',
'schoolmasters', 'shaken', 'biology', 'inclination', 'immoderate',
'stem', 'allegory', 'economical', 'daytime', 'Newell', 'Moscow',
'archeology', 'ported', 'scandals', 'Blackfoot', 'leery', 'kilobit',
'empire', 'obliviousness', 'productions', 'sacrificed', 'ideals',
'enrolling', 'certainties', 'Capsicum', 'Brookdale', 'Markism',
'unkind', 'dyers', 'legislates', 'grotesquely', 'megawords',
'arbitrary', 'laughing', 'wildcats', 'thrower', 'sex', 'devils',
'Wehr', 'ablates', 'consume', 'gossips', 'doorways', 'Shari',
'advanced', 'enumerable', 'existentially', 'stunt', 'auctioneers',
'scheduler', 'blanching', 'petulance', 'perceptibly', 'vapors',
'progressed', 'rains', 'intercom', 'emergency', 'increased',
'fluctuating', 'Krishna', 'silken', 'reformed', 'transformation',
'easter', 'fares', 'comprehensible', 'trespasses', 'hallmark',
'tormenter', 'breastworks', 'brassiere', 'bladders', 'civet', 'death',
'transformer', 'tolerably', 'bugle', 'clergy', 'mantels', 'satin',
'Boswellizes', 'Bloomington', 'notifier', 'Filippo', 'circling',
'unassigned', 'dumbness', 'sentries', 'representativeness', 'souped',
'Klux', 'Kingstown', 'gerund', 'Russell', 'splices', 'bellow',
'bandies', 'beefers', 'cameramen', 'appalled', 'Ionian', 'butterball',
'Portland', 'pleaded', 'admiringly', 'pricks', 'hearty', 'corer',
'deliverable', 'accountably', 'mentors', 'accorded',
'acknowledgement', 'Lawrenceville', 'morphology', 'eucalyptus',
'Rena', 'enchanting', 'tighter', 'scholars', 'graduations', 'edges',
'Latinization', 'proficiency', 'monolithic', 'parenthesizing', 'defy',
'shames', 'enjoyment', 'Purdue', 'disagrees', 'barefoot', 'maims',
'flabbergast', 'dishonorable', 'interpolation', 'fanatics', 'dickens',
'abysses', 'adverse', 'components', 'bowl', 'belong', 'Pipestone',
'trainees', 'paw', 'pigtail', 'feed', 'whore', 'conditioner',
'Volstead', 'voices', 'strain', 'inhabits', 'Edwin', 'discourses',
'deigns', 'cruiser', 'biconvex', 'biking', 'depreciation', 'Harrison',
'Persian', 'stunning', 'agar', 'rope', 'wagoner', 'elections',
'reticulately', 'Cruz', 'pulpits', 'wilt', 'peels', 'plants',
'administerings', 'deepen', 'rubs', 'hence', 'dissension', 'implored',
'bereavement', 'abyss', 'Pennsylvania', 'benevolent', 'corresponding',
'Poseidon', 'inactive', 'butchers', 'Mach', 'woke', 'loading',
'utilizing', 'Hoosier', 'undo', 'Semitization', 'trigger', 'Mouthe',
'mark', 'disgracefully', 'copier', 'futility', 'gondola', 'algebraic',
'lecturers', 'sponged', 'instigators', 'looted', 'ether', 'trust',
'feeblest', 'sequencer', 'disjointness', 'congresses', 'Vicksburg',
'incompatibilities', 'commend', 'Luxembourg', 'reticulation',
'instructively', 'reconstructs', 'bricks', 'attache', 'Englishman',
'provocation', 'roughen', 'cynic', 'plugged', 'scrawls', 'antipode',
'injected', 'Daedalus', 'Burnsides', 'asker', 'confronter',
'merriment', 'disdain', 'thicket', 'stinker', 'great', 'tiers',
'oust', 'antipodes', 'Macintosh', 'tented', 'packages',
'Mediterraneanize', 'hurts', 'orthodontist', 'seeder', 'readying',
'babying', 'Florida', 'Sri', 'buckets', 'complementary',
'cartographer', 'chateaus', 'shaves', 'thinkable', 'Tehran',
'Gordian', 'Angles', 'arguable', 'bureau', 'smallest', 'fans',
'navigated', 'dipole', 'bootleg', 'distinctive', 'minimization',
'absorbed', 'surmised', 'Malawi', 'absorbent', 'close', 'conciseness',
'hopefully', 'declares', 'descent', 'trick', 'portend', 'unable',
'mildly', 'Morse', 'reference', 'scours', 'Caribbean', 'battlers',
'astringency', 'likelier', 'Byronizes', 'econometric', 'grad',
'steak', 'Austrian', 'ban', 'voting', 'Darlington', 'bison', 'Cetus',
'proclaim', 'Gilbertson', 'evictions', 'submittal', 'bearings',
'Gothicizer', 'settings', 'McMahon', 'densities', 'determinants',
'period', 'DeKastere', 'swindle', 'promptness', 'enablers', 'wordy',
'during', 'tables', 'responder', 'baffle', 'phosgene', 'muttering',
'limiters', 'custodian', 'prevented', 'Stouffer', 'waltz', 'Videotex',
'brainstorms', 'alcoholism', 'jab', 'shouldering', 'screening',
'explicitly', 'earner', 'commandment', 'French', 'scrutinizing',
'Gemma', 'capacitive', 'sheriff', 'herbivore', 'Betsey', 'Formosa',
'scorcher', 'font', 'damming', 'soldiers', 'flack', 'Marks',
'unlinking', 'serenely', 'rotating', 'converge', 'celebrities',
'unassailable', 'bawling', 'wording', 'silencing', 'scotch',
'coincided', 'masochists', 'graphs', 'pernicious', 'disease',
'depreciates', 'later', 'torus', 'interject', 'mutated', 'causer',
'messy', 'Bechtel', 'redundantly', 'profoundest', 'autopsy',
'philosophic', 'iterate', 'Poisson', 'horridly', 'silversmith',
'millennium', 'plunder', 'salmon', 'missioner', 'advances', 'provers',
'earthliness', 'manor', 'resurrectors', 'Dahl', 'canto', 'gangrene',
'gabler', 'ashore', 'frictionless', 'expansionism', 'emphasis',
'preservations', 'Duane', 'descend', 'isolated', 'firmware',
'dynamites', 'scrawled', 'cavemen', 'ponder', 'prosperity', 'squaw',
'vulnerable', 'opthalmic', 'Simms', 'unite', 'totallers', 'Waring',
'enforced', 'bridge', 'collecting', 'sublime', 'Moore', 'gobble',
'criticizes', 'daydreams', 'sedate', 'apples', 'Concordia',
'subsequence', 'distill', 'Allan', 'seizure', 'Isadore', 'Lancashire',
'spacings', 'corresponded', 'hobble', 'Boonton', 'genuineness',
'artifact', 'gratuities', 'interviewee', 'Vladimir', 'mailable',
'Bini', 'Kowalewski', 'interprets', 'bereave', 'evacuated', 'friend',
'tourists', 'crunched', 'soothsayer', 'fleetly', 'Romanizations',
'Medicaid', 'persevering', 'flimsy', 'doomsday', 'trillion',
'carcasses', 'guess', 'seersucker', 'ripping', 'affliction',
'wildest', 'spokes', 'sheaths', 'procreate', 'rusticates', 'Schapiro',
'thereafter', 'mistakenly', 'shelf', 'ruination', 'bushel',
'assuredly', 'corrupting', 'federation', 'portmanteau', 'wading',
'incendiary', 'thing', 'wanderers', 'messages', 'Paso', 'reexamined',
'freeings', 'denture', 'potting', 'disturber', 'laborer', 'comrade',
'intercommunicating', 'Pelham', 'reproach', 'Fenton', 'Alva', 'oasis',
'attending', 'cockpit', 'scout', 'Jude', 'gagging', 'jailed',
'crustaceans', 'dirt', 'exquisitely', 'Internet', 'blocker', 'smock',
'Troutman', 'neighboring', 'surprise', 'midscale', 'impart',
'badgering', 'fountain', 'Essen', 'societies', 'redresses',
'afterwards', 'puckering', 'silks', 'Blakey', 'sequel', 'greet',
'basements', 'Aubrey', 'helmsman', 'album', 'wheelers', 'easternmost',
'flock', 'ambassadors', 'astatine', 'supplant', 'gird', 'clockwork',
'foxes', 'rerouting', 'divisional', 'bends', 'spacer',
'physiologically', 'exquisite', 'concerts', 'unbridled', 'crossing',
'rock', 'leatherneck', 'Fortescue', 'reloading', 'Laramie', 'Tim',
'forlorn', 'revert', 'scarcer', 'spigot', 'equality', 'paranormal',
'aggrieves', 'pegs', 'committeewomen', 'documented', 'interrupt',
'emerald', 'Battelle', 'reconverted', 'anticipated', 'prejudices',
'drowsiness', 'trivialities', 'food', 'blackberries', 'Cyclades',
'tourist', 'branching', 'nugget', 'Asilomar', 'repairmen', 'Cowan',
'receptacles', 'nobler', 'Nebraskan', 'territorial', 'chickadee',
'bedbug', 'darted', 'vigilance', 'Octavia', 'summands', 'policemen',
'twirls', 'style', 'outlawing', 'specifiable', 'pang', 'Orpheus',
'epigram', 'Babel', 'butyrate', 'wishing', 'fiendish', 'accentuate',
'much', 'pulsed', 'adorned', 'arbiters', 'counted', 'Afrikaner',
'parameterizes', 'agenda', 'Americanism', 'referenda', 'derived',
'liquidity', 'trembling', 'lordly', 'Agway', 'Dillon', 'propellers',
'statement', 'stickiest', 'thankfully', 'autograph', 'parallel',
'impulse', 'Hamey', 'stylistic', 'disproved', 'inquirer', 'hoisting',
'residues', 'variant', 'colonials', 'dequeued', 'especial', 'Samoa',
'Polaris', 'dismisses', 'surpasses', 'prognosis', 'urinates',
'leaguers', 'ostriches', 'calculative', 'digested', 'divided',
'reconfigurer', 'Lakewood', 'illegalities', 'redundancy',
'approachability', 'masterly', 'cookery', 'crystallized', 'Dunham',
'exclaims', 'mainline', 'Australianizes', 'nationhood', 'pusher',
'ushers', 'paranoia', 'workstations', 'radiance', 'impedes',
'Minotaur', 'cataloging', 'bites', 'fashioning', 'Alsop', 'servants',
'Onondaga', 'paragraph', 'leadings', 'clients', 'Latrobe',
'Cornwallis', 'excitingly', 'calorimetric', 'savior', 'tandem',
'antibiotics', 'excuse', 'brushy', 'selfish', 'naive', 'becomes',
'towers', 'popularizes', 'engender', 'introducing', 'possession',
'slaughtered', 'marginally', 'Packards', 'parabola', 'utopia',
'automata', 'deterrent', 'chocolates', 'objectives', 'clannish',
'aspirin', 'ferociousness', 'primarily', 'armpit', 'handfuls',
'dangle', 'Manila', 'enlivened', 'decrease', 'phylum', 'hardy',
'objectively', 'baskets', 'chaired', 'Sepoy', 'deputy', 'blizzard',
'shootings', 'breathtaking', 'sticking', 'initials', 'epitomized',
'Forrest', 'cellular', 'amatory', 'radioed', 'horrified', 'Neva',
'simultaneous', 'delimiter', 'expulsion', 'Himmler', 'contradiction',
'Remus', 'Franklinizations', 'luggage', 'moisture', 'Jews',
'comptroller', 'brevity', 'contradictions', 'Ohio', 'active',
'babysit', 'China', 'youngest', 'superstition', 'clawing', 'raccoons',
'chose', 'shoreline', 'helmets', 'Jeffersonian', 'papered',
'kindergarten', 'reply', 'succinct', 'split', 'wriggle', 'suitcases',
'nonce', 'grinders', 'anthem', 'showcase', 'maimed', 'blue', 'obeys',
'unreported', 'perusing', 'recalculate', 'rancher', 'demonic',
'Lilliputianize', 'approximation', 'repents', 'yellowness',
'irritates', 'Ferber', 'flashlights', 'booty', 'Neanderthal',
'someday', 'foregoes', 'lingering', 'cloudiness', 'guy', 'consumer',
'Berkowitz', 'relics', 'interpolating', 'reappearing', 'advisements',
'Nolan', 'turrets', 'skeletal', 'skills', 'mammas', 'Winsett',
'wheelings', 'stiffen', 'monkeys', 'plainness', 'braziers', 'Leary',
'advisee', 'jack', 'verb', 'reinterpret', 'geometrical', 'trolleys',
'arboreal', 'overpowered', 'Cuzco', 'poetical', 'admirations',
'Hobbes', 'phonemes', 'Newsweek', 'agitator', 'finally', 'prophets',
'environment', 'easterners', 'precomputed', 'faults', 'rankly',
'swallowing', 'crawl', 'trolley', 'spreading', 'resourceful', 'go',
'demandingly', 'broader', 'spiders', 'Marsha', 'debris', 'operates',
'Dundee', 'alleles', 'crunchier', 'quizzical', 'hanging', 'Fisk']

wordsd = {}
for word in words:
    wordsd[word] = 1


def collect_options(args, jobs, options):

    while args:
        arg = args.pop(0)
        if arg.startswith('-'):
            name = arg[1:]
            if name == 'options':
                fname = args.pop(0)
                d = {}
                execfile(fname, d)
                collect_options(list(d['options']), jobs, options)
            elif options.has_key(name):
                v = args.pop(0)
                if options[name] != None:
                    raise ValueError(
                        "Duplicate values for %s, %s and %s"
                        % (name, v, options[name])
                        )
                options[name] = v
            elif name == 'setup':
                options['setup'] = 1
            elif globals().has_key(name.capitalize()+'Job'):
                job = name
                kw = {}
                while args and args[0].find("=") > 0:
                    arg = args.pop(0).split('=')
                    name, v = arg[0], '='.join(arg[1:])
                    if kw.has_key(name):
                        raise ValueError(
                            "Duplicate parameter %s for job %s"
                            % (name, job)
                            )
                    kw[name]=v
                if kw.has_key('frequency'):
                    frequency = kw['frequency']
                    del kw['frequency']
                else:
                    frequency = 1

                if kw.has_key('sleep'):
                    sleep = float(kw['sleep'])
                    del kw['sleep']
                else:
                    sleep = 0.0001

                if kw.has_key('repeat'):
                    repeatp = float(kw['repeat'])
                    del kw['repeat']
                else:
                    repeatp = 0

                jobs.append((job, kw, frequency, sleep, repeatp))
            else:
                raise ValueError("not an option or job", name)
        else:
            raise ValueError("Expected an option", arg)


def find_lib_python():
    for b in os.getcwd(), os.path.split(sys.argv[0])[0]:
        for i in range(6):
            d = ['..']*i + ['lib', 'python']
            p = os.path.join(b, *d)
            if os.path.isdir(p):
                return p
    raise ValueError("Couldn't find lib/python")

def main(args=None):
    lib_python = find_lib_python()
    sys.path.insert(0, lib_python)

    if args is None:
        args = sys.argv[1:]
    if not args:
        print __doc__
        sys.exit(0)

    print args
    random.seed(hash(tuple(args))) # always use the same for the given args

    options = {"mbox": None, "threads": None}
    jobdefs = []
    collect_options(args, jobdefs, options)

    mboxes = {}
    if options["mbox"]:
        mboxes[options["mbox"]] = MBox(options["mbox"])

    if options.has_key('setup'):
        setup(lib_python)
    else:
        import Zope
        Zope.startup()

    #from ThreadedAsync.LoopCallback import loop
    #threading.Thread(target=loop, args=(), name='asyncore').start()

    jobs = JobProducer()
    for job, kw, frequency, sleep, repeatp in jobdefs:
        Job = globals()[job.capitalize()+'Job']
        if getattr(Job, 'needs_mbox', 0):
            if not kw.has_key("mbox"):
                if not options["mbox"]:
                    raise ValueError(
                        "no mailbox (mbox option) file  specified")
                kw['mbox'] = mboxes[options["mbox"]]
            else:
                if not mboxes.has_key[kw["mbox"]]:
                    mboxes[kw['mbox']] = MBox[kw['mbox']]
                kw["mbox"] = mboxes[kw['mbox']]
        jobs.add(Job(**kw), frequency, sleep, repeatp)

    if not jobs:
        print "No jobs to execute"
        return

    threads = int(options['threads'] or '0')
    if threads > 1:
        threads = [threading.Thread(target=run, args=(jobs, i), name=str(i))
                   for i in range(threads)]
        for thread in threads:
            thread.start()
        for thread in threads:
            thread.join()
    else:
        run(jobs)


if __name__ == '__main__':
    main()


=== ZODB3/Tools/README.txt 1.1.2.1 => 1.1.2.2 ===
--- ZODB3/Tools/README.txt:1.1.2.1	Fri Aug 15 15:59:42 2003
+++ ZODB3/Tools/README.txt	Wed Aug 27 16:09:20 2003
@@ -6,23 +6,27 @@
 Unless otherwise noted, these scripts are invoked with the name of the
 Data.fs file as their only argument.  Example: checkbtrees.py data.fs.
 
+
 analyze.py -- A transaction analyzer for FileStorage
 
 Reports on the data in a FileStorage.  The report is organized by
 class.  It shows total data, as well as separate reports for current
 and historical revisions of objects.
 
+
 checkbtrees.py -- Checks BTrees in a FileStorage for corruption.
 
 Attempts to find all the BTrees contained in a Data.fs and calls their
 _check() methods.
 
+
 fsdump.py -- Summarize FileStorage contents, one line per revision.
 
 Prints a report of FileStorage contents, with one line for each
 transaction and one line for each data record in that transaction.
 Includes time stamps, file positions, and class names.
 
+
 fstest.py -- Simple consistency checker for FileStorage
 
 usage: fstest.py [-v] data.fs
@@ -41,6 +45,7 @@
 possible for the damage to occur only in the part of the file that
 stores object pickles.  Those errors will go undetected.
 
+
 netspace.py -- Hackish attempt to report on size of objects
 
 usage: netspace.py [-P | -v] data.fs
@@ -51,6 +56,7 @@
 Traverses objects from the database root and attempts to calculate
 size of object, including all reachable subobjects.
 
+
 parsezeolog.py -- Parse BLATHER logs from ZEO server.
 
 This script may be obsolete.  It has not been tested against the
@@ -59,10 +65,12 @@
 Reports on the time and size of transactions committed by a ZEO
 server, by inspecting log messages at BLATHER level.
 
+
 repozo.py -- Incremental backup utility for FileStorage.
 
 Run the script with the -h option to see usage details.
 
+
 timeout.py -- Script to test transaction timeout
 
 usage: timeout.py address delay [storage-name]
@@ -71,17 +79,20 @@
 and tpc_vote(), and then sleeps forever.  This should trigger the
 transaction timeout feature of the server.
 
+
 zeopack.py -- Script to pack a ZEO server.
 
 The script connects to a server and calls pack() on a specific
 storage.  See the script for usage details.
 
+
 zeoreplay.py -- Experimental script to replay transactions from a ZEO log.
 
 Like parsezeolog.py, this may be obsolete because it was written
 against an earlier version of the ZEO server.  See the script for
 usage details.
 
+
 zeoup.py
 
 Usage: zeoup.py [options]
@@ -90,6 +101,18 @@
 attempt to update the zeoup counter in the root.  It will report
 success if it updates to counter or if it gets a ConflictError.  A
 ConflictError is considered a success, because the client was able to
-start a transaction. 
+start a transaction.
 
 See the script for details about the options.
+
+
+zodbload.py - exercise ZODB under a heavy synthesized Zope-like load
+
+See the module docstring for details.  Note that this script requires
+Zope.  New in ZODB3 3.1.4.
+
+
+zeoserverlog.py - analyze ZEO server log for performance statistics
+
+See the module docstring for details; there are a large number of
+options.  New in ZODB3 3.1.4.
\ No newline at end of file




More information about the Zodb-checkins mailing list