root/vendor/zasync/branches/z29-nux/bforests.txt

Revision 31431, 5.0 kB (checked in by rspivak, 4 years ago)

Importing initial vendor 1.1 drop

  • Property svn:eol-style set to native
Line 
1 bforests are dictionary-like objects that use multiple BTrees for a backend and
2 support rotation of the composite trees.  This supports various implementations
3 of timed member expirations, enabling caches and semi-persistent storage.  A
4 useful and simple subclass would be to promote a key-value pair to the
5 first (newest) bucket whenever the key is accessed, for instance.
6
7 Like btrees, bforests come in four flavors: Integer-Integer (IIBForest),
8 Integer-Object (IOBForest), Object-Integer (OIBForest), and Object-Object
9 (OOBForest).  The examples here will deal with them in the abstract: we will
10 create classes from the imaginary and representative BForest class, and
11 generate keys from KeyGenerator and values from ValueGenerator.  From the
12 examples you should be able to extrapolate usage of all four types.
13
14 First let's instantiate a bforest and look at an empty example.  By default,
15 a new bforest creates two composite btree buckets.
16
17 >>> d = BForest()
18 >>> list(d.keys())
19 []
20 >>> list(d.values())
21 []
22 >>> len(d.buckets)
23 2
24 >>> dummy_key = KeyGenerator()
25 >>> d.get(dummy_key)
26 >>> d.get(dummy_key, 42)
27 42
28
29 Now we'll populate it.  We'll first create a dictionary we'll use to compare.
30
31 >>> original = {}
32 >>> for i in range(10):
33 ...     original[KeyGenerator()] = ValueGenerator()
34 ...
35 >>> d.update(original)
36 >>> d == original
37 True
38 >>> d_keys = list(d.keys())
39 >>> d_keys.sort()
40 >>> o_keys = original.keys()
41 >>> o_keys.sort()
42 >>> d_keys == o_keys
43 True
44 >>> d_values = list(d.values())
45 >>> d_values.sort()
46 >>> o_values = original.values()
47 >>> o_values.sort()
48 >>> o_values == d_values
49 True
50 >>> d_items = list(d.items())
51 >>> d_items.sort()
52 >>> o_items = original.items()
53 >>> o_items.sort()
54 >>> o_items == d_items
55 True
56 >>> key, value = d.popitem()
57 >>> value == original.pop(key)
58 True
59 >>> key, value = original.popitem()
60 >>> value == d.pop(key)
61 True
62 >>> len(d) == len(original)
63 True
64
65 Now let's rotate the buckets.
66
67 >>> d.rotateBucket()
68
69 ...and we'll do the exact same test as above, first.
70
71 >>> d == original
72 True
73 >>> d_keys = list(d.keys())
74 >>> d_keys.sort()
75 >>> o_keys = original.keys()
76 >>> o_keys.sort()
77 >>> d_keys == o_keys
78 True
79 >>> d_values = list(d.values())
80 >>> d_values.sort()
81 >>> o_values = original.values()
82 >>> o_values.sort()
83 >>> o_values == d_values
84 True
85 >>> d_items = list(d.items())
86 >>> d_items.sort()
87 >>> o_items = original.items()
88 >>> o_items.sort()
89 >>> o_items == d_items
90 True
91 >>> key, value = d.popitem()
92 >>> value == original.pop(key)
93 True
94 >>> key, value = original.popitem()
95 >>> value == d.pop(key)
96 True
97 >>> len(d) == len(original)
98 True
99
100 Now we'll make a new dictionary to represent changes made after the bucket
101 rotation.
102
103 >>> second = {}
104 >>> for i in range(10):
105 ...     key = KeyGenerator()
106 ...     value = ValueGenerator()
107 ...     second[key] = value
108 ...     d[key] = value
109 ...
110 >>> original.update(second)
111
112 ...and we'll do almost the exact same test as above, first.
113
114 >>> d == original
115 True
116 >>> d_keys = list(d.keys())
117 >>> d_keys.sort()
118 >>> o_keys = original.keys()
119 >>> o_keys.sort()
120 >>> d_keys == o_keys
121 True
122 >>> d_values = list(d.values())
123 >>> d_values.sort()
124 >>> o_values = original.values()
125 >>> o_values.sort()
126 >>> o_values == d_values
127 True
128 >>> d_items = list(d.items())
129 >>> d_items.sort()
130 >>> o_items = original.items()
131 >>> o_items.sort()
132 >>> o_items == d_items
133 True
134 >>> key, value = d.popitem()
135 >>> ignore = second.pop(key, None) # keep second up-to-date
136 >>> value == original.pop(key)
137 True
138 >>> key, value = original.popitem()
139 >>> ignore = second.pop(key, None) # keep second up-to-date
140 >>> value == d.pop(key)
141 True
142 >>> len(d) == len(original)
143 True
144
145 Now if we rotate the buckets, the first set of items will be gone, but the
146 second will remain.
147
148 >>> d.rotateBucket()
149 >>> d == original
150 False
151 >>> d == second
152 True
153
154 Let's set a value, check the copy behavior,  and then rotate it one more time.
155
156 >>> third = {KeyGenerator(): ValueGenerator()}
157 >>> d.update(third)
158 >>> copy = d.copy()
159 >>> copy == d
160 True
161 >>> copy != second # because second doesn't have the values of third
162 True
163 >>> list(copy.buckets[0].items()) == list(d.buckets[0].items())
164 True
165 >>> list(copy.buckets[1].items()) == list(d.buckets[1].items())
166 True
167 >>> copy[KeyGenerator()] = ValueGenerator()
168 >>> copy == d
169 False
170 >>> d.rotateBucket()
171 >>> d == third
172 True
173 >>> d.clear()
174 >>> d == BForest() == {}
175 True
176
177 >>> d.update(second)
178
179 We'll make a value in one bucket that we'll override in another.
180
181 >>> d[third.keys()[0]] = ValueGenerator()
182 >>> d.rotateBucket()
183 >>> d.update(third)
184 >>> second.update(third)
185 >>> d == second
186 True
187
188 Notice this unpleasant bit: a bforest and dict with the same contents will
189 return True if the comparison is bforest == dict (above) but false if the
190 comparison is dict == bforest.  This might be fixable if we could use rich
191 comparisons, but we can't do that with the old-style ExtensionClass used in
192 pre-Zope 2.8.
193
194 >>> second == d
195 False
196
197 The tree method converts the bforest to a btree as efficiently as I know how
198 for a common case of more items in buckets than buckets.
199
200 >>> tree = d.tree()
201 >>> d_items = list(d.items())
202 >>> d_items.sort()
203 >>> t_items = list(tree.items())
204 >>> t_items.sort()
205 >>> t_items == d_items
206 True
207
Note: See TracBrowser for help on using the browser.