Commit 98ad5c09 authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent 515968ad
......@@ -65,52 +65,6 @@ session example:
S: 03d85dd871718899
...
allstructs
----------
`treegen allstructs` generates topologies for subset of all possible tree
changes in between two trees specified by two key->value dicts.
For every kv the following tree topologies are considered: 1) native (the one
that ZODB would usually create natively via regular usage), and 2) n random
ones. Then tree topologies are emitted corresponding to tree1->tree2 and
tree1<-tree2 transitions for all combinations of (tree1, tree2) pairs.
The output of `treegen allstructs` is valid input for `treegen trees`.
Allstructs protocol specification:
S: # allstructs.srv start
C: <maxdepth> <maxsplit> <n>(/<seed>) <kv1> <kv2>
S: # allstructs <kv1> <kv2>
S: # maxdepth=<maxdepth> maxsplit=<maxsplit> n=<n> seed=<seed>
S: <tree1₀>
S: <tree2₀>
S: <tree1₁>
...
S: # ----
session example:
# allstructs.srv start
1 1 10 1:a 2:b
# allstructs 1:a 2:b
# maxdepth=1 maxsplit=1 n=10 seed=1591369961
T/B1:a
T/B2:b
T/T/B1:a
T/B2:b
T/B1:a
T/T/B2:b
T/T/B1:a
T/T/B2:b
T/B1:a
# ----
--------
(*) 300-500ms, see https://github.com/pypa/setuptools/issues/510.
"""
from __future__ import print_function, absolute_import
......@@ -244,133 +198,6 @@ def TreesSrv(zstor, r):
xprint("%s" % ashex(head))
# AllStructsSrv is server version of AllStructs.
@func
def AllStructsSrv(r):
xprint('# allstructs.srv start')
for req in xreadlines(r):
# maxdepth maxsplit n(/seed) kv1 kv2
maxdepth, maxsplit, n, kv1txt, kv2txt = req.split()
maxdepth = int(maxdepth)
maxsplit = int(maxsplit)
seed = None
if '/' in n:
n, seeds = n.split('/')
seed = int(seeds)
n = int(n)
if kv1txt == 'ø': kv1txt = ''
if kv2txt == 'ø': kv2txt = ''
AllStructs(kv1txt, kv2txt, maxdepth, maxsplit, n, seed)
xprint('# ----')
# AllStructs generates topologies for subset of all possible tree changes in
# between kv1 and kv2. See top-level documentation for details.
@func
def AllStructs(kv1txt, kv2txt, maxdepth, maxsplit, n, seed=None):
zstor = MappingStorage() # in RAM storage to create native ZODB topologies
zctx = ZCtx(zstor)
defer(zctx.close)
kv1 = kvDecode(kv1txt, zctx.vdecode)
kv2 = kvDecode(kv2txt, zctx.vdecode)
print("# allstructs %s %s" % (kv1txt, kv2txt))
# create the tree
ztree = zctx.root['ztree'] = LOBTree()
commit('init')
# initial kv1 and kv2 states with topologies prepared as ZODB would do natively
patch(ztree, diff({}, kv1), verify=kv1)
if kv1 == {}: ztree._p_changed = True # to avoid empty commit - see TreesSrv
commit('kv1')
t1struct0 = xbtree.StructureOf(ztree)
patch(ztree, diff(kv1, kv2), verify=kv2)
if kv2 == kv1: ztree._p_changed = True
commit('kv2')
t2struct0 = xbtree.StructureOf(ztree)
# all tree topologies that can represent kv1 and kv2
t1AllStructs = list(xbtree.AllStructs(kv1.keys(), maxdepth, maxsplit, kv=kv1))
t2AllStructs = list(xbtree.AllStructs(kv2.keys(), maxdepth, maxsplit, kv=kv2))
# seed
if seed is None:
seed = time.now()
seed = int(seed)
random.seed(seed)
print("# maxdepth=%d maxsplit=%d n=%d seed=%d" % (maxdepth, maxsplit, n, seed))
# all tree1 and tree2 topologies jumps in between we are going to emit:
# native + n random ones.
if t1struct0 in t1AllStructs: t1AllStructs.remove(t1struct0) # avoid dups
if t2struct0 in t2AllStructs: t2AllStructs.remove(t2struct0)
t1structv = [t1struct0] + random.sample(t1AllStructs, min(n, len(t1AllStructs)))
t2structv = [t2struct0] + random.sample(t2AllStructs, min(n, len(t2AllStructs)))
# emit topologies for tree1->tree2 and tree1<-tree2 transitions for all
# combinations of tree1 and tree2.
t12travel = list(bitravel2Way(t1structv, t2structv))
for i,tstruct in enumerate(t12travel):
if i%2 == 0:
assert tstruct in t1structv
else:
assert tstruct in t2structv
print(zctx.TopoEncode(tstruct))
# bitravel2Way generates travel path through all A<->B edges such
# that all edges a->b and a<-b are traveled and exactly once.
#
# The travel starts from A[0].
def bitravel2Way(A, B): # -> i[] of node
na = len(A); assert na > 0
nb = len(B); assert nb > 0
yield A[0] # A₀
for j in range(nb):
yield B[j] # A₀ -> Bj
for i in range(1,na):
yield A[i] # Ai <- Bj
yield B[j] # Ai -> Bj
yield A[0] # A₀ <- Bj
def test_bitravel2Way():
a,b,c = 'a','b','c'
A = [a,b,c]
B = [1, 2]
got = list(bitravel2Way(A, B))
want = [a,1,b,1,c,1,a,2,b,2,c,2,a]
assert got == want, (got, want)
test_bitravel2Way()
# kvEncode encodes key->value mapping into text.
# e.g. {1:'a', 2:'b'} -> '1:a,2:b'
def kvEncode(kvDict, vencode): # -> kvText
retv = []
for k in sorted(kvDict.keys()):
v = kvDict[k]
retv.append('%d:%s' % (k, vencode(v)))
return ','.join(retv)
# kvDecode decodes key->value mapping from text.
# e.g. '1:a,2:b' -> {1:'a', 2:'b'}
def kvDecode(kvText, vdecode): # -> kvDict
if kvText == "":
return {}
kv = {}
for item in kvText.split(','):
ktxt, vtxt = item.split(':')
k = int(ktxt)
v = vdecode(vtxt)
if k in kv:
raise ValueError("key %s present multiple times" % k)
kv[k] = v
return kv
# diff computes difference in between mappings d1 and d2.
......@@ -453,19 +280,6 @@ def xreadlines(r):
yield l
@func
def cmd_allstructs(argv):
if argv == ['-h']:
print("Usage: cat requests |treegen allstructs", file=sys.stderr)
print(" treegen allstructs <request>", file=sys.stderr)
sys.exit(0)
r = sys.stdin
if len(argv) != 0:
r = six.StringIO(' '.join(argv))
AllStructsSrv(r)
@func
def cmd_trees(argv):
if argv in ([], ['-h']):
......@@ -492,7 +306,6 @@ def xprint(msg):
cmdRegistry = {
'allstructs': cmd_allstructs,
'trees': cmd_trees,
}
......
......@@ -91,14 +91,6 @@ type TreeSrv struct {
head zodb.Tid // last made commit
}
// AllStructsSrv represents connection to running `treegen allstructs` server.
//
// Create it with StartAllStructsSrv().
// - AllStructs(maxdepth, maxsplit, n, seed, kv1, kv2)
type AllStructsSrv struct {
*TreeGenSrv
}
// StartTreeGenSrv spawns `treegen ...` server.
func StartTreeGenSrv(argv ...string) (_ *TreeGenSrv, hello string, err error) {
defer xerr.Contextf(&err, "treesrv %v: start", argv)
......@@ -186,30 +178,6 @@ func StartTreeSrv(zurl string) (_ *TreeSrv, err error) {
return tg, nil
}
// StartAllStructsSrv spawns `treegen allstructs` server.
func StartAllStructsSrv() (_ *AllStructsSrv, err error) {
defer xerr.Context(&err, "allstructs.srv: start")
tgSrv, hello, err := StartTreeGenSrv("allstructs")
if err != nil {
return nil, err
}
sg := &AllStructsSrv{TreeGenSrv: tgSrv}
defer func() {
if err != nil {
tgSrv.Close() // ignore error
}
}()
defer xerr.Contextf(&err, "invalid hello %q", hello)
if hello != "# allstructs.srv start" {
return nil, fmt.Errorf("unexpected format")
}
return sg, nil
}
// Commit creates new commit with underlying tree changed to specified tree topology.
func (tg *TreeSrv) Commit(tree string) (_ zodb.Tid, err error) {
......@@ -237,38 +205,6 @@ func (tg *TreeSrv) Commit(tree string) (_ zodb.Tid, err error) {
return tid, nil
}
// AllStructs returns response from `treegen allstructs`
func (tg *AllStructsSrv) AllStructs(kv1, kv2 map[Key]string, maxdepth, maxsplit, n int, seed int64) (_ []string, err error) {
req := fmt.Sprintf("%d %d %d/%d %s %s", maxdepth, maxsplit, n, seed, kvtxt(kv1), kvtxt(kv2))
defer xerr.Contextf(&err, "allstructs.srv: %s", req)
_, err = io.WriteString(tg.pyin, req + "\n")
if err != nil {
return nil, err
}
structv := []string{}
for {
reply, err := tg.pyout.ReadString('\n')
if err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return nil, err
}
reply = strings.TrimSuffix(reply, "\n")
if reply == "# ----" {
return structv, nil // end of response
}
if strings.HasPrefix(reply, "#") {
continue // comment
}
structv = append(structv, reply)
}
}
// RTree represents Tree node covering [lo, hi_] key range in its parent tree.
type RTree struct {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment