203
203
temp_file = builder.finish()
204
204
content = temp_file.read()
206
self.assertEqual(10646, len(content))
206
self.assertEqual(9602, len(content))
207
207
self.assertEqual(
208
208
"B+Tree Graph Index 2\nnode_ref_lists=0\nkey_elements=1\nlen=800\n"
209
209
"row_lengths=1,2\n",
215
215
expected_root = (
216
216
"type=internal\n"
218
"503503503503503503503503503503503503503503503503503503503503503503503503503503503503503503503503503503503503503503503503\n"
218
) + ("635" * 40) + "\n"
220
219
self.assertEqual(expected_root, root_bytes)
221
220
# We already know serialisation works for leaves, check key selection:
222
221
leaf1_bytes = zlib.decompress(leaf1)
223
222
sorted_node_keys = sorted(node[0] for node in nodes)
224
223
node = btree_index._LeafNode(leaf1_bytes, 1, 0)
225
self.assertEqual(448, len(node.keys))
226
self.assertEqual(sorted_node_keys[:448], sorted(node.keys))
224
self.assertEqual(594, len(node.keys))
225
self.assertEqual(sorted_node_keys[:594], sorted(node.keys))
227
226
leaf2_bytes = zlib.decompress(leaf2)
228
227
node = btree_index._LeafNode(leaf2_bytes, 1, 0)
229
self.assertEqual(800 - 448, len(node.keys))
230
self.assertEqual(sorted_node_keys[448:], sorted(node.keys))
228
self.assertEqual(800 - 594, len(node.keys))
229
self.assertEqual(sorted_node_keys[594:], sorted(node.keys))
232
231
def test_last_page_rounded_1_layer(self):
233
232
builder = btree_index.BTreeBuilder(key_elements=1, reference_lists=0)
260
259
temp_file = builder.finish()
261
260
content = temp_file.read()
263
self.assertEqual(10646, len(content))
262
self.assertEqual(9602, len(content))
264
263
self.assertEqual(
265
264
"B+Tree Graph Index 2\nnode_ref_lists=0\nkey_elements=1\nlen=800\n"
266
265
"row_lengths=1,2\n",
269
268
leaf2 = content[8192:]
270
269
leaf2_bytes = zlib.decompress(leaf2)
271
270
node = btree_index._LeafNode(leaf2_bytes, 1, 0)
272
self.assertEqual(800 - 448, len(node.keys))
271
self.assertEqual(800 - 594, len(node.keys))
273
272
sorted_node_keys = sorted(node[0] for node in nodes)
274
self.assertEqual(sorted_node_keys[448:], sorted(node.keys))
273
self.assertEqual(sorted_node_keys[594:], sorted(node.keys))
276
275
def test_three_level_tree_details(self):
277
276
# The left most pointer in the second internal node in a row should
278
277
# pointer to the second node that the internal node is for, _not_
279
278
# the first, otherwise the first node overlaps with the last node of
280
279
# the prior internal node on that row.
281
# We will be adding 100,000 nodes, so spill at 100,001 to prevent
280
# We will be adding 140,000 nodes, so spill at 200,001 to prevent
282
281
# having to flush anything out to disk.
283
282
builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=2,
285
# 100K nodes is *just* enough to create a two internal nodes on the
284
# 140K nodes is *just* enough to create a two internal nodes on the
287
nodes = self.make_nodes(50000, 2, 2)
286
nodes = self.make_nodes(70000, 2, 2)
289
288
for node in nodes:
290
289
builder.add_node(*node)
321
320
temp_file = builder.finish()
322
321
content = temp_file.read()
324
self.assertEqual(10574, len(content))
323
self.assertEqual(10639, len(content))
325
324
self.assertEqual(
326
325
"B+Tree Graph Index 2\nnode_ref_lists=2\nkey_elements=2\nlen=400\n"
327
326
"row_lengths=1,2\n",
334
333
"type=internal\n"
336
335
"1111111111111111111111111111111111111111\x00"
337
"126126126126126126126126126126126126126126126126126126126"
338
"126126126126126126126126126126126126126126126126126126126126126\n"
336
) + ("151" * 40) + "\n"
340
337
self.assertEqual(expected_root, root_bytes)
341
338
# We assume the other leaf nodes have been written correctly - layering
594
591
# The entire index should have been read, as it is one page long.
595
592
self.assertEqual([('readv', 'index', [(0, size)], False, None)],
596
593
transport._activity)
597
self.assertEqual(1593, size)
594
self.assertEqual(1199, size)
599
596
def test_2_levels_key_count_2_2(self):
600
597
builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=2)
603
600
builder.add_node(*node)
604
601
transport = get_transport('trace+' + self.get_url(''))
605
602
size = transport.put_file('index', builder.finish())
606
self.assertEqual(10242, size)
603
self.assertEqual(9152, size)
607
604
index = btree_index.BTreeGraphIndex(transport, 'index', size)
608
605
del transport._activity[:]
609
606
self.assertEqual([], transport._activity)
626
623
# The entire index should have been read linearly.
627
624
self.assertEqual([('readv', 'index', [(0, size)], False, None)],
628
625
transport._activity)
629
self.assertEqual(3846, size)
626
self.assertEqual(2768, size)
631
628
def test_validate_two_pages(self):
632
629
builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=2)
636
633
transport = get_transport('trace+' + self.get_url(''))
637
634
size = transport.put_file('index', builder.finish())
638
635
# Root page, 2 leaf pages
639
self.assertEqual(10242, size)
636
self.assertEqual(9152, size)
640
637
index = btree_index.BTreeGraphIndex(transport, 'index', size)
641
638
del transport._activity[:]
642
639
self.assertEqual([], transport._activity)
644
641
# The entire index should have been read linearly.
645
642
self.assertEqual([('readv', 'index', [(0, 4096)], False, None),
646
('readv', 'index', [(4096, 4096), (8192, 2050)], False, None)],
643
('readv', 'index', [(4096, 4096), (8192, 960)], False, None)],
647
644
transport._activity)
648
645
# XXX: TODO: write some badly-ordered nodes, and some pointers-to-wrong
649
646
# node and make validate find them.
687
684
# iterating all entries reads the header, then does a linear
689
686
builder = btree_index.BTreeBuilder(key_elements=2, reference_lists=2,
691
# 100k nodes is enough to create a three-level index, which shows that
688
# 140k nodes is enough to create a three-level index, which shows that
692
689
# we skip the internal nodes and just read the leaf nodes.
693
nodes = self.make_nodes(50000, 2, 2)
690
nodes = self.make_nodes(70000, 2, 2)
694
691
for node in nodes:
695
692
builder.add_node(*node)
696
693
transport = get_transport('trace+' + self.get_url(''))
697
694
size = transport.put_file('index', builder.finish())
698
self.assertEqual(2026722, size, 'number of expected bytes in the'
695
self.assertEqual(2624681, size, 'number of expected bytes in the'
699
696
' output changed')
701
698
index = btree_index.BTreeGraphIndex(transport, 'index', size)
702
699
del transport._activity[:]
703
700
self.assertEqual([], transport._activity)
704
found_nodes = list(index.iter_all_entries())
701
found_nodes = self.time(list, index.iter_all_entries())
706
703
for node in found_nodes:
707
704
self.assertTrue(node[0] is index)
709
706
self.assertEqual(3, len(index._row_lengths),
710
707
"Not enough rows: %r" % index._row_lengths)
711
708
# Should be as long as the nodes we supplied
712
self.assertEqual(100000, len(found_nodes))
709
self.assertEqual(140000, len(found_nodes))
713
710
# Should have the same content
714
711
self.assertEqual(set(nodes), set(bare_nodes))
715
712
# Should have done linear scan IO up the index, ignoring
717
714
# The entire index should have been read
718
715
total_pages = sum(index._row_lengths)
719
716
self.assertEqual(total_pages, index._row_offsets[-1])
720
self.assertEqual(2026722, size)
717
self.assertEqual(2624681, size)
721
718
# The start of the leaves
722
719
first_byte = index._row_offsets[-2] * btree_index._PAGE_SIZE
723
720
readv_request = []
724
721
for offset in range(first_byte, size, 4096):
725
722
readv_request.append((offset, 4096))
726
723
# The last page is truncated
727
readv_request[-1] = (readv_request[-1][0], 3298)
724
readv_request[-1] = (readv_request[-1][0], 2624681 % 4096)
728
725
expected = [('readv', 'index', [(0, 4096)], False, None),
729
726
('readv', 'index', readv_request, False, None)]
730
727
if expected != transport._activity: