[11949] | 1 | """Helpers for maintainers of ikoba sites. |
---|
[8417] | 2 | |
---|
| 3 | XXX: This stuff might go into a separate package, but right |
---|
| 4 | now it is too less for a complete package. |
---|
| 5 | """ |
---|
| 6 | import sys |
---|
[10182] | 7 | from ZODB.FileStorage import FileStorage |
---|
| 8 | from ZODB.scripts.analyze import report, analyze, shorten |
---|
[8718] | 9 | from zope.component.hooks import setSite |
---|
| 10 | from zope.component import getUtility |
---|
| 11 | from zope.catalog.interfaces import ICatalog |
---|
| 12 | from zope.intid.interfaces import IIntIds |
---|
[8417] | 13 | |
---|
[10182] | 14 | |
---|
[8417] | 15 | def db_analyze(args=None): |
---|
| 16 | """Run the analyze tool from ZODB package. |
---|
| 17 | """ |
---|
| 18 | if args is None: |
---|
| 19 | args = sys.argv[1:] |
---|
| 20 | |
---|
| 21 | path = None |
---|
| 22 | if len(args) > 0: |
---|
| 23 | path = args[0] |
---|
| 24 | else: |
---|
| 25 | print |
---|
| 26 | print "Analyze a ZODB file and print statistics" |
---|
| 27 | print "about contained objects, sizes, etc." |
---|
| 28 | print |
---|
| 29 | print "Usage: %s <path-to-Data.fs>" % sys.argv[0] |
---|
| 30 | print |
---|
| 31 | sys.exit(0) |
---|
| 32 | |
---|
| 33 | report(analyze(path)) |
---|
[8718] | 34 | |
---|
| 35 | def update_catalog(site, cat_name, objects=[], func=None): |
---|
| 36 | """Update a catalog. |
---|
| 37 | |
---|
| 38 | Put `objects` or objects delivered by `func()` into the catalog |
---|
| 39 | registered under `cat_name` in `site`. |
---|
| 40 | |
---|
| 41 | Objects to be catalogued must be 'located', i.e. they must have a |
---|
| 42 | __name__ and __parent__ (because they are adapted to |
---|
| 43 | IKeyReference). |
---|
| 44 | |
---|
| 45 | You can pass in objects as some iterable or as a function that is |
---|
| 46 | called to deliver the set of objects to be catalogued. |
---|
| 47 | |
---|
| 48 | A function takes precedence over object lists. |
---|
| 49 | """ |
---|
| 50 | setSite(site) |
---|
| 51 | cat = getUtility(ICatalog, name=cat_name) |
---|
| 52 | intids = getUtility(IIntIds, context=cat) |
---|
| 53 | if func is not None: |
---|
| 54 | objects = func() |
---|
| 55 | for ob in objects: |
---|
| 56 | doc_id = intids.queryId(ob, None) |
---|
| 57 | if doc_id is None: |
---|
| 58 | doc_id = intids.register(ob) |
---|
| 59 | cat.index_doc(doc_id, ob) |
---|
| 60 | return cat |
---|
[10182] | 61 | |
---|
| 62 | def db_diff(args=None): |
---|
| 63 | """Run the analyze tool from ZODB package to find diffs between |
---|
| 64 | two ZODBs. |
---|
| 65 | """ |
---|
| 66 | if args is None: |
---|
| 67 | args = sys.argv[1:] |
---|
| 68 | |
---|
| 69 | zodb_path1, zodb_path2 = (None, None) |
---|
| 70 | if len(args) > 1: |
---|
| 71 | zodb_path1, zodb_path2 = args[0:2] |
---|
| 72 | else: |
---|
| 73 | print |
---|
| 74 | print "Analyze two ZODB files and print statistics" |
---|
| 75 | print "about contained objects, sizes, etc." |
---|
| 76 | print "Shows only differences." |
---|
| 77 | print |
---|
| 78 | print "Usage: %s <path-to-Data1.fs> <path-to-Data2.fs>" % sys.argv[0] |
---|
| 79 | print |
---|
| 80 | sys.exit(0) |
---|
| 81 | |
---|
| 82 | a1 = analyze(zodb_path1) |
---|
| 83 | a2 = analyze(zodb_path2) |
---|
| 84 | |
---|
| 85 | diff_report(a1, a2) |
---|
| 86 | |
---|
| 87 | |
---|
| 88 | def diff_report(rep1, rep2): |
---|
| 89 | print "Processed %d (%d, %d) records in %d (%d, %d) transactions" % ( |
---|
| 90 | rep1.OIDS + rep2.OIDS, rep1.OIDS, rep2.OIDS, |
---|
| 91 | rep1.TIDS + rep2.TIDS, rep2.TIDS, rep2.TIDS) |
---|
| 92 | print "Types used:" |
---|
| 93 | fmt = " %-44s %7s %9s %6s %7s" |
---|
| 94 | fmtp = "%-44s %+7d %9d %5.1f%% %7.2f" # per-class format |
---|
| 95 | fmtpplus = "+ %-44s %+7d %+9d %+5.1f%% %7.2f" # per-class format |
---|
| 96 | fmtpminus = "- %-44s %+7d %+9d %+5.1f%% %7.2f" # per-class format |
---|
| 97 | fmts = " %44s %+7d %+8dk %+5.1f%% %+7.2f" # summary format |
---|
| 98 | typemap1, typemap2 = rep1.TYPEMAP.keys(), rep2.TYPEMAP.keys() |
---|
| 99 | typemap1.sort() |
---|
| 100 | typemap2.sort() |
---|
| 101 | typemap = list(set(typemap1 + typemap2)) |
---|
| 102 | typemap.sort() |
---|
| 103 | print fmt % ("Class Name", "Count", "TBytes", "Pct", "AvgSize") |
---|
| 104 | print fmt % ('-'*44, '-'*7, '-'*9, '-'*5, '-'*7) |
---|
| 105 | for t in typemap: |
---|
| 106 | if t in typemap1 and t in typemap2 and ( |
---|
| 107 | rep1.TYPESIZE[t] == rep2.TYPESIZE[t]) and ( |
---|
| 108 | rep1.TYPEMAP[t] == rep2.TYPEMAP[t]): |
---|
| 109 | continue |
---|
| 110 | if t not in typemap1: |
---|
| 111 | cnt = rep2.TYPEMAP[t] |
---|
| 112 | pct = rep2.TYPESIZE[t] * 100.0 / rep2.DBYTES |
---|
| 113 | print fmtpplus % (shorten(t, 44), cnt, rep2.TYPESIZE[t], |
---|
| 114 | pct, rep2.TYPESIZE[t] * 1.0 / rep2.TYPEMAP[t]) |
---|
| 115 | |
---|
| 116 | elif t not in typemap2: |
---|
| 117 | cnt = -rep1.TYPEMAP[t] |
---|
| 118 | pct = rep1.TYPESIZE[t] * 100.0 / rep1.DBYTES |
---|
| 119 | print fmtpminus % (shorten(t, 44), cnt, -rep1.TYPESIZE[t], |
---|
| 120 | pct, rep1.TYPESIZE[t] * 1.0 / rep1.TYPEMAP[t]) |
---|
| 121 | else: |
---|
| 122 | cnt = rep2.TYPEMAP[t] - rep1.TYPEMAP[t] |
---|
| 123 | pct1 = rep1.TYPESIZE[t] * 100.0 / rep1.DBYTES |
---|
| 124 | pct2 = rep2.TYPESIZE[t] * 100.0 / rep2.DBYTES |
---|
| 125 | pct = pct2 - pct1 |
---|
| 126 | size = rep2.TYPESIZE[t] - rep1.TYPESIZE[t] |
---|
| 127 | if cnt > 0: |
---|
| 128 | print fmtpplus % ( |
---|
| 129 | shorten(t, 44), cnt, size, |
---|
| 130 | pct, rep2.TYPESIZE[t] * 1.0 / rep2.TYPEMAP[t]) |
---|
| 131 | else: |
---|
| 132 | print fmtpminus % ( |
---|
| 133 | shorten(t, 44), cnt, size, |
---|
| 134 | pct, rep1.TYPESIZE[t] * 1.0 / rep1.TYPEMAP[t]) |
---|
| 135 | |
---|
| 136 | |
---|
| 137 | print fmt % ('='*44, '='*7, '='*9, '='*5, '='*7) |
---|
| 138 | print fmts % ('Current Objects', rep2.COIDS - rep1.COIDS, |
---|
| 139 | (rep2.CBYTES / 1024.0) - (rep1.CBYTES / 1024.0), |
---|
| 140 | (rep2.CBYTES * 100.0 / rep2.DBYTES) - ( |
---|
| 141 | rep1.CBYTES * 100.0 / rep1.DBYTES), |
---|
| 142 | (rep2.CBYTES * 1.0 / rep2.COIDS) - ( |
---|
| 143 | rep1.CBYTES * 1.0 / rep1.COIDS)) |
---|
| 144 | #if rep1.FOIDS and rep2.FOIDS: |
---|
| 145 | fbytes = (rep2.FBYTES / 1024.0) - (rep1.FBYTES / 1024.0) |
---|
| 146 | rep1_fpct = (rep1.DBYTES and |
---|
| 147 | (rep1.FBYTES * 100.0 / (rep1.DBYTES)) or 0.0) |
---|
| 148 | rep2_fpct = (rep2.DBYTES and |
---|
| 149 | (rep2.FBYTES * 100.0 / (rep2.DBYTES)) or 0.0) |
---|
| 150 | print fmts % ('Old Objects', rep2.FOIDS - rep1.FOIDS, |
---|
| 151 | fbytes, rep2_fpct - rep1_fpct, |
---|
| 152 | (rep2.FBYTES * 1.0 / (rep2.FOIDS or 1.0)) - ( |
---|
| 153 | rep1.FBYTES * 1.0 / (rep1.FOIDS or 1.0))) |
---|
| 154 | return |
---|
| 155 | |
---|