Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix and finish page recycling #863

Merged
merged 3 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/Soil-Core-Tests/SoilBTreeTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -274,12 +274,12 @@ SoilBTreeTest >> testIndexRewriting [
1 to: capacity + 1 do: [ :n |
index at: n put: (n asByteArrayOfSize: 8) ].
index flush.
self assert: index headerPage lastPageIndex equals: 3.
self assert: index headerPage lastPageOffset equals: 3.
self assert: (index pageAt: 3) items size equals: 128.
self assert: (index pageAt: 1) items size equals: 126.
fileSizeBefore := index path size.
index newPluggableRewriter rewrite.
self assert: index headerPage lastPageIndex equals: 3.
self assert: index headerPage lastPageOffset equals: 3.
self assert: (index pageAt: 3) items size equals: 128.
self assert: (index pageAt: 1) items size equals: 126.

Expand All @@ -299,7 +299,7 @@ SoilBTreeTest >> testIndexRewritingWithCleaning [
index at: n put: SoilObjectId removed ].

index newPluggableRewriter cleanRemoved run.
self assert: index headerPage lastPageIndex equals: 2.
self assert: index headerPage lastPageOffset equals: 2.
self assert: (index pageAt: 1) items size equals: 1.
self assert: (index at: capacity + 1) asByteArray equals: (capacity + 1 asByteArrayOfSize: 8).

Expand Down
12 changes: 6 additions & 6 deletions src/Soil-Core-Tests/SoilSkipListPageTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ SoilSkipListPageTest >> testHeaderPageFull [
offset: 1;
level: 8;
pageSize: 4096;
lastPageIndex: 2.
lastPageOffset: 2.
1 to: 253 do: [ :n |
page addItem: (n -> n) ].
self assert: page hasRoom equals: false.
Expand All @@ -146,7 +146,7 @@ SoilSkipListPageTest >> testHeaderPageFullStreamSize [
keySize: 7;
valueSize: 8;
maxLevel: 8;
lastPageIndex: 1;
lastPageOffset: 1;
offset: 1;
level: 6;
pageSize: 4096.
Expand All @@ -171,7 +171,7 @@ SoilSkipListPageTest >> testHeaderPageSizeOfHeader [
offset: 1;
level: 8;
pageSize: 4096;
lastPageIndex: 2.
lastPageOffset: 2.
bytes := ByteArray streamContents: [ :stream |
page writeHeaderOn: stream ].
self assert: bytes size equals: page headerSize
Expand All @@ -186,7 +186,7 @@ SoilSkipListPageTest >> testHeaderPageWriteAndRead [
maxLevel: 12;
offset: 1;
level: 8;
lastPageIndex: 2.
lastPageOffset: 2.
self assert: page needsWrite.
bytes := ByteArray streamContents: [ :stream |
page writeOn: stream ].
Expand All @@ -196,7 +196,7 @@ SoilSkipListPageTest >> testHeaderPageWriteAndRead [
self assert: readPage keySize equals: 16.
self assert: readPage valueSize equals: 8.
self assert: readPage maxLevel equals: 12.
self assert: readPage lastPageIndex equals: 2.
self assert: readPage lastPageOffset equals: 2.
self deny: readPage needsWrite
]

Expand All @@ -210,7 +210,7 @@ SoilSkipListPageTest >> testWriteOldVersion [
offset: 1;
level: 8;
pageSize: 4096;
lastPageIndex: 2;
lastPageOffset: 2;
instVarNamed: #version put: 1;
yourself.
1 to: 10 do: [ :n |
Expand Down
110 changes: 108 additions & 2 deletions src/Soil-Core-Tests/SoilSkipListTest.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,112 @@ SoilSkipListTest >> testFirstArgLargerThenSize [
self assert: (index first: 5) size equals: 4
]

{ #category : #tests }
SoilSkipListTest >> testFreePageAdd [
| iterator |
iterator := index newIterator.
1 to: 2500 do: [ :n |
iterator at: n put: (n asByteArrayOfSize: 8) ].
self assert: index headerPage lastPageOffset equals: 10.
self assert: index headerPage firstFreePageIndex equals: 0.
500 to: 1500 do: [ : n |
iterator removeKey: n ].
self assert: index headerPage lastPageOffset equals: 10.
self assert: index headerPage firstFreePageIndex equals: 0.
index cleanUpToVersion: nil.
self assert: index headerPage lastPageOffset equals: 10.
"the first page that gets empty is reused as free list page"
self assert: index headerPage firstFreePageIndex equals: 3.
"subsequent removed pages are added to the first page"
self assertCollection: (index store pageAt: 3) pageIndexes hasSameElements: #( 4 5 ).
]

{ #category : #tests }
SoilSkipListTest >> testFreePageAddNested [
| iterator nestedFreeIndexes |
index
maxLevel: 8;
valueSize: 512.
iterator := index newIterator.
"create enough pages to test"
1 to: 9000 do: [ :n |
iterator at: n put: (n asByteArrayOfSize: 8) ].
self assert: index headerPage lastPageOffset equals: 1286.
self assert: index headerPage firstFreePageIndex equals: 0.
"now we free more pages than a free page list has capacity"
10 to: 8990 do: [ : n |
iterator removeKey: n ].
index cleanUpToVersion: nil.
self assert: index headerPage firstFreePageIndex equals: 3.
self assert: (index store pageAt: 3) pageIndexes size equals: 1018.
"as there were more than free list capacity there is a next free
list page"
self assert: (index store pageAt: 3) next equals: 1022.
nestedFreeIndexes := ((index store pageAt: 1022) pageIndexes copy) copyWith: 1022.
"we fill the index with enough elements to use all free pages including the nested
ones"
1 to: 15*1018 do: [ :n |
iterator at: n put: (n asByteArrayOfSize: 8) ].
"there should be no more free page"
self assert: index headerPage firstFreePageIndex equals: 0.
"and all pageIndexes of the next free page should be included"
self assert: ((index store pages collect: #offset) includesAll: nestedFreeIndexes)


]

{ #category : #tests }
SoilSkipListTest >> testFreePageReuse [
| iterator |
iterator := index newIterator.
1 to: 2500 do: [ :n |
iterator at: n put: (n asByteArrayOfSize: 8) ].
self assert: index headerPage lastPageOffset equals: 10.
self assert: index headerPage firstFreePageIndex equals: 0.
500 to: 1500 do: [ : n |
iterator removeKey: n ].
index cleanUpToVersion: nil.
self assert: index headerPage lastPageOffset equals: 10.
2501 to: 2750 do: [ :n |
iterator at: n put: (n asByteArrayOfSize: 8) ].
index cleanUpToVersion: nil.
"first free page is #3 with content #( 4 5 ). Adding 250 entries should remove
one page #4 for reuse"
self assertCollection: (index store pageAt: 3) pageIndexes hasSameElements: #( 5 ).

]

{ #category : #tests }
SoilSkipListTest >> testFreePageReuseAtEndAppend [
| iterator |
iterator := index newIterator.
1 to: 2500 do: [ :n |
iterator at: n put: (n asByteArrayOfSize: 8) ].
self assert: index headerPage lastPageOffset equals: 10.
self assert: index headerPage firstFreePageIndex equals: 0.
500 to: 1500 do: [ : n |
iterator removeKey: n ].
index cleanUpToVersion: nil.
self assert: index headerPage lastPageOffset equals: 10.
self assertCollection: (index store pageAt: 3) pageIndexes hasSameElements: #( 4 5 ).
iterator := index newIterator.
2501 to: 3500 do: [ :n |
iterator at: n put: (n asByteArrayOfSize: 8) ].
index cleanUpToVersion: nil.
"readding the same amount of entries but at the end should reuse all
free pages making first free 0."
self assert: index headerPage firstFreePageIndex equals: 0.
"as the new entries did not fit exactly we have one more page at the
end which should have update the header page"
self assert: index headerPage lastPageOffset equals: 11.
"page #3 was the first free page so it was recycled last. Therefor the
page #3 should point to page #11 which should be the last"
self assert: (index store pageAt: 3) right first equals: 11.
self assert: (index store pageAt: 11) right first equals: 0.


]

{ #category : #tests }
SoilSkipListTest >> testIndexRewriting [

Expand All @@ -275,7 +381,7 @@ SoilSkipListTest >> testIndexRewriting [
index flush.
fileSizeBefore := index path size.
index newPluggableRewriter rewrite.
self assert: index headerPage lastPageIndex equals: 2.
self assert: index headerPage lastPageOffset equals: 2.
self assert: (index pageAt: 2) items size equals: 1.
"self assert: index path size equals: fileSizeBefore"
]
Expand All @@ -292,7 +398,7 @@ SoilSkipListTest >> testIndexRewritingWithCleaning [
index at: n put: SoilObjectId removed ].

index newPluggableRewriter cleanRemoved rewrite.
self assert: index headerPage lastPageIndex equals: 1.
self assert: index headerPage lastPageOffset equals: 1.
self assert: (index pageAt: 1) items size equals: 1.
self assert: (index at: capacity + 1) equals: (capacity + 1 asByteArrayOfSize: 8).

Expand Down
20 changes: 10 additions & 10 deletions src/Soil-Core/SoilBTreeHeaderPage.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ Class {
#name : #SoilBTreeHeaderPage,
#superclass : #SoilAbstractBTreeDataPage,
#instVars : [
'lastPageIndex',
'firstFreePageIndex',
'size'
'size',
'lastPageOffset'
],
#category : #'Soil-Core-Index-BTree'
}
Expand Down Expand Up @@ -68,13 +68,13 @@ SoilBTreeHeaderPage >> isHeaderPage [
]

{ #category : #accessing }
SoilBTreeHeaderPage >> lastPageIndex [
^ lastPageIndex
SoilBTreeHeaderPage >> lastPageOffset [
^ lastPageOffset
]

{ #category : #accessing }
SoilBTreeHeaderPage >> lastPageIndex: anObject [
lastPageIndex := anObject.
SoilBTreeHeaderPage >> lastPageOffset: anObject [
lastPageOffset := anObject.
needWrite := true
]

Expand All @@ -85,9 +85,9 @@ SoilBTreeHeaderPage >> latestVersion [

{ #category : #accessing }
SoilBTreeHeaderPage >> nextPageOffset [
lastPageIndex := lastPageIndex + 1.
lastPageOffset := lastPageOffset + 1.
needWrite := true.
^ lastPageIndex
^ lastPageOffset
]

{ #category : #printing }
Expand All @@ -100,7 +100,7 @@ SoilBTreeHeaderPage >> readFrom: aStream [
super readFrom: aStream.
keySize := (aStream next: 2) asInteger.
valueSize := (aStream next: 2) asInteger.
lastPageIndex :=(aStream next: self pointerSize) asInteger.
lastPageOffset :=(aStream next: self pointerSize) asInteger.
firstFreePageIndex :=(aStream next: 4) asInteger.
size := (aStream next: 6) asInteger.
self readItemsFrom: aStream
Expand All @@ -122,7 +122,7 @@ SoilBTreeHeaderPage >> writeHeaderOn: aStream [
aStream
nextPutAll: (keySize asByteArrayOfSize: 2);
nextPutAll: (valueSize asByteArrayOfSize: 2);
nextPutAll: (lastPageIndex asByteArrayOfSize: self pointerSize);
nextPutAll: (lastPageOffset asByteArrayOfSize: self pointerSize);
nextPutAll: (firstFreePageIndex asByteArrayOfSize: 4);
nextPutAll: (self size asByteArrayOfSize: 6).
]
9 changes: 7 additions & 2 deletions src/Soil-Core/SoilBasicBTree.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ SoilBasicBTree >> keySize: anInteger [
SoilBasicBTree >> newHeaderPage [
^ SoilBTreeHeaderPage new
offset: 1;
lastPageIndex: 2;
lastPageOffset: 2;
firstFreePageIndex: 0;
pageSize: self pageSize
]
Expand Down Expand Up @@ -135,7 +135,7 @@ SoilBasicBTree >> nextFreePage [
^ firstPage hasFreePages
ifTrue: [
freePage := store pageAt: firstPage removeFirstIndex.
self addDirtyPage: freePage.
self addDirtyPage: firstPage.
freePage ]
ifFalse: [
self headerPage firstFreePageIndex: 0.
Expand All @@ -154,6 +154,11 @@ SoilBasicBTree >> pageClass [
^ SoilBTreeDataPage
]

{ #category : #removing }
SoilBasicBTree >> removeDirtyPage: aPage [
dirtyPages remove: aPage
]

{ #category : #removing }
SoilBasicBTree >> removeKey: key ifAbsent: aBlock [

Expand Down
13 changes: 11 additions & 2 deletions src/Soil-Core/SoilBasicSkipList.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,16 @@ SoilBasicSkipList class >> isAbstract [

{ #category : #adding }
SoilBasicSkipList >> addDirtyPage: aPage [
dirtyPages add: aPage
dirtyPages at: aPage offset put: aPage
]

{ #category : #'as yet unclassified' }
SoilBasicSkipList >> cleanUpToVersion: aNumberOrNil [
SoilIndexCleaner new
index: self;
readVersion: aNumberOrNil;
clean.

]

{ #category : #testing }
Expand All @@ -30,7 +39,7 @@ SoilBasicSkipList >> hasHeaderPage [
{ #category : #initialization }
SoilBasicSkipList >> initialize [
super initialize.
dirtyPages := Set new
dirtyPages := OrderedDictionary new
]

{ #category : #accessing }
Expand Down
2 changes: 1 addition & 1 deletion src/Soil-Core/SoilIndex.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ SoilIndex >> recyclePage: aPage [

{ #category : #removing }
SoilIndex >> removeDirtyPage: aPage [
dirtyPages remove: aPage
dirtyPages removeKey: aPage offset
]

{ #category : #removing }
Expand Down
2 changes: 1 addition & 1 deletion src/Soil-Core/SoilIndexCleaner.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ SoilIndexCleaner >> clean [
that need to be written"
self dirtyPages do: [ :page |
"if no transaction needs old state and the page needs cleaning"
((self canDiscardOldState: page) and: [ page needsCleanup ]) ifTrue: [
(page needsCleanup and: [ self canDiscardOldState: page ]) ifTrue: [
self cleanPage: page ] ].
"Now we have a final set if dirty pages"
index dirtyPages copy do: [ :page |
Expand Down
2 changes: 1 addition & 1 deletion src/Soil-Core/SoilIndexConsistencyVisitor.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ SoilIndexConsistencyVisitor >> check: aSoil [
{ #category : #visiting }
SoilIndexConsistencyVisitor >> visitPagedFileIndexStore: aSoilPagedFileIndexStore [
| numberOfPages |
numberOfPages := aSoilPagedFileIndexStore headerPage lastPageIndex.
numberOfPages := aSoilPagedFileIndexStore headerPage lastPageOffset.
1 to: numberOfPages do: [ :pageIndex | | page |
page := aSoilPagedFileIndexStore pageAt: pageIndex.
(page right allSatisfy: #isZero) ifTrue: [
Expand Down
2 changes: 1 addition & 1 deletion src/Soil-Core/SoilInstanceVisitor.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ SoilInstanceVisitor >> visitObjectId: aSoilObjectId [
{ #category : #visiting }
SoilInstanceVisitor >> visitPagedFileIndexStore: aSoilPagedFileIndexStore [
| numberOfPages |
numberOfPages := aSoilPagedFileIndexStore headerPage lastPageIndex.
numberOfPages := aSoilPagedFileIndexStore headerPage lastPageOffset.
1 to: numberOfPages do: [ :pageIndex | | page |
page := aSoilPagedFileIndexStore pageAt: pageIndex.
page items do: [ :item |
Expand Down
4 changes: 2 additions & 2 deletions src/Soil-Core/SoilPagedIndexStore.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ SoilPagedIndexStore >> isOpen [
]

{ #category : #accessing }
SoilPagedIndexStore >> lastPageIndex [
^ self headerPage lastPageIndex
SoilPagedIndexStore >> lastPageOffset [
^ self headerPage lastPageOffset
]

{ #category : #'instance creation' }
Expand Down
Loading
Loading