Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张蕾
Geth-Modification
Commits
331de17e
Unverified
Commit
331de17e
authored
6 years ago
by
Martin Holst Swende
Committed by
Péter Szilágyi
5 years ago
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
core/rawdb: support starting offset for future deletion
parent
80469bea
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
175 additions
and
30 deletions
+175
-30
freezer_table.go
core/rawdb/freezer_table.go
+56
-9
freezer_table_test.go
core/rawdb/freezer_table_test.go
+119
-21
No files found.
core/rawdb/freezer_table.go
View file @
331de17e
...
...
@@ -81,9 +81,14 @@ type freezerTable struct {
head
*
os
.
File
// File descriptor for the data head of the table
files
map
[
uint32
]
*
os
.
File
// open files
headId
uint32
// number of the currently active head file
tailId
uint32
// number of the earliest file
index
*
os
.
File
// File descriptor for the indexEntry file of the table
items
uint64
// Number of items stored in the table
// In the case that old items are deleted (from the tail), we use itemOffset
// to count how many historic items have gone missing.
items
uint64
// Number of items stored in the table (including items removed from tail)
itemOffset
uint32
// Offset (number of discarded items)
headBytes
uint32
// Number of bytes written to the head file
readMeter
metrics
.
Meter
// Meter for measuring the effective amount of data read
writeMeter
metrics
.
Meter
// Meter for measuring the effective amount of data written
...
...
@@ -164,10 +169,19 @@ func (t *freezerTable) repair() error {
// Open the head file
var
(
firstIndex
indexEntry
lastIndex
indexEntry
contentSize
int64
contentExp
int64
)
// Read index zero, determine what file is the earliest
// and what item offset to use
t
.
index
.
ReadAt
(
buffer
,
0
)
firstIndex
.
unmarshalBinary
(
buffer
)
t
.
tailId
=
firstIndex
.
offset
t
.
itemOffset
=
firstIndex
.
filenum
t
.
index
.
ReadAt
(
buffer
,
offsetsSize
-
indexEntrySize
)
lastIndex
.
unmarshalBinary
(
buffer
)
t
.
head
,
err
=
t
.
openFile
(
lastIndex
.
filenum
,
os
.
O_RDWR
|
os
.
O_CREATE
|
os
.
O_APPEND
)
...
...
@@ -225,7 +239,7 @@ func (t *freezerTable) repair() error {
return
err
}
// Update the item and byte counters and return
t
.
items
=
uint64
(
offsetsSize
/
indexEntrySize
-
1
)
// last indexEntry points to the end of the data file
t
.
items
=
uint64
(
t
.
itemOffset
)
+
uint64
(
offsetsSize
/
indexEntrySize
-
1
)
// last indexEntry points to the end of the data file
t
.
headBytes
=
uint32
(
contentSize
)
t
.
headId
=
lastIndex
.
filenum
...
...
@@ -245,7 +259,7 @@ func (t *freezerTable) preopen() (err error) {
// The repair might have already opened (some) files
t
.
releaseFilesAfter
(
0
,
false
)
// Open all except head in RDONLY
for
i
:=
uint32
(
0
);
i
<
t
.
headId
;
i
++
{
for
i
:=
uint32
(
t
.
tailId
);
i
<
t
.
headId
;
i
++
{
if
_
,
err
=
t
.
openFile
(
i
,
os
.
O_RDONLY
);
err
!=
nil
{
return
err
}
...
...
@@ -259,7 +273,8 @@ func (t *freezerTable) preopen() (err error) {
func
(
t
*
freezerTable
)
truncate
(
items
uint64
)
error
{
t
.
lock
.
Lock
()
defer
t
.
lock
.
Unlock
()
// If out item count is corrent, don't do anything
// If our item count is correct, don't do anything
if
atomic
.
LoadUint64
(
&
t
.
items
)
<=
items
{
return
nil
}
...
...
@@ -275,6 +290,7 @@ func (t *freezerTable) truncate(items uint64) error {
}
var
expected
indexEntry
expected
.
unmarshalBinary
(
buffer
)
// We might need to truncate back to older files
if
expected
.
filenum
!=
t
.
headId
{
// If already open for reading, force-reopen for writing
...
...
@@ -290,7 +306,6 @@ func (t *freezerTable) truncate(items uint64) error {
t
.
head
=
newHead
atomic
.
StoreUint32
(
&
t
.
headId
,
expected
.
filenum
)
}
if
err
:=
t
.
head
.
Truncate
(
int64
(
expected
.
offset
));
err
!=
nil
{
return
err
}
...
...
@@ -330,9 +345,9 @@ func (t *freezerTable) openFile(num uint32, flag int) (f *os.File, err error) {
if
f
,
exist
=
t
.
files
[
num
];
!
exist
{
var
name
string
if
t
.
noCompression
{
name
=
fmt
.
Sprintf
(
"%s.%d.rdat"
,
t
.
name
,
num
)
name
=
fmt
.
Sprintf
(
"%s.%
04
d.rdat"
,
t
.
name
,
num
)
}
else
{
name
=
fmt
.
Sprintf
(
"%s.%d.cdat"
,
t
.
name
,
num
)
name
=
fmt
.
Sprintf
(
"%s.%
04
d.cdat"
,
t
.
name
,
num
)
}
f
,
err
=
os
.
OpenFile
(
filepath
.
Join
(
t
.
path
,
name
),
flag
,
0644
)
if
err
!=
nil
{
...
...
@@ -376,11 +391,13 @@ func (t *freezerTable) Append(item uint64, blob []byte) error {
t
.
lock
.
RLock
()
// Ensure the table is still accessible
if
t
.
index
==
nil
||
t
.
head
==
nil
{
t
.
lock
.
RUnlock
()
return
errClosed
}
// Ensure only the next item can be written, nothing else
if
atomic
.
LoadUint64
(
&
t
.
items
)
!=
item
{
panic
(
fmt
.
Sprintf
(
"appending unexpected item: want %d, have %d"
,
t
.
items
,
item
))
t
.
lock
.
RUnlock
()
return
fmt
.
Errorf
(
"appending unexpected item: want %d, have %d"
,
t
.
items
,
item
)
}
// Encode the blob and write it into the data file
if
!
t
.
noCompression
{
...
...
@@ -461,13 +478,20 @@ func (t *freezerTable) Retrieve(item uint64) ([]byte, error) {
if
atomic
.
LoadUint64
(
&
t
.
items
)
<=
item
{
return
nil
,
errOutOfBounds
}
// Ensure the item was not deleted from the tail either
offset
:=
atomic
.
LoadUint32
(
&
t
.
itemOffset
)
if
uint64
(
offset
)
>
item
{
return
nil
,
errOutOfBounds
}
t
.
lock
.
RLock
()
startOffset
,
endOffset
,
filenum
,
err
:=
t
.
getBounds
(
item
)
startOffset
,
endOffset
,
filenum
,
err
:=
t
.
getBounds
(
item
-
uint64
(
offset
)
)
if
err
!=
nil
{
t
.
lock
.
RUnlock
()
return
nil
,
err
}
dataFile
,
exist
:=
t
.
files
[
filenum
]
if
!
exist
{
t
.
lock
.
RUnlock
()
return
nil
,
fmt
.
Errorf
(
"missing data file %d"
,
filenum
)
}
// Retrieve the data itself, decompress and return
...
...
@@ -499,3 +523,26 @@ func (t *freezerTable) Sync() error {
}
return
t
.
head
.
Sync
()
}
// printIndex is a debug print utility function for testing
func
(
t
*
freezerTable
)
printIndex
()
{
buf
:=
make
([]
byte
,
indexEntrySize
)
fmt
.
Printf
(
"|-----------------|
\n
"
)
fmt
.
Printf
(
"| fileno | offset |
\n
"
)
fmt
.
Printf
(
"|--------+--------|
\n
"
)
for
i
:=
uint64
(
0
);
;
i
++
{
if
_
,
err
:=
t
.
index
.
ReadAt
(
buf
,
int64
(
i
*
indexEntrySize
));
err
!=
nil
{
break
}
var
entry
indexEntry
entry
.
unmarshalBinary
(
buf
)
fmt
.
Printf
(
"| %03d | %03d |
\n
"
,
entry
.
filenum
,
entry
.
offset
)
if
i
>
100
{
fmt
.
Printf
(
" ...
\n
"
)
break
}
}
fmt
.
Printf
(
"|-----------------|
\n
"
)
}
This diff is collapsed.
Click to expand it.
core/rawdb/freezer_table_test.go
View file @
331de17e
...
...
@@ -19,12 +19,13 @@ package rawdb
import
(
"bytes"
"fmt"
"github.com/ethereum/go-ethereum/metrics"
"math/rand"
"os"
"path/filepath"
"testing"
"time"
"github.com/ethereum/go-ethereum/metrics"
)
func
init
()
{
...
...
@@ -32,10 +33,10 @@ func init() {
}
// Gets a chunk of data, filled with 'b'
func
getChunk
(
size
int
,
b
byte
)
[]
byte
{
func
getChunk
(
size
int
,
b
int
)
[]
byte
{
data
:=
make
([]
byte
,
size
)
for
i
,
_
:=
range
data
{
data
[
i
]
=
b
data
[
i
]
=
b
yte
(
b
)
}
return
data
}
...
...
@@ -61,7 +62,7 @@ func TestFreezerBasics(t *testing.T) {
}
defer
f
.
Close
()
// Write 15 bytes 255 times, results in 85 files
for
x
:=
byte
(
0
)
;
x
<
255
;
x
++
{
for
x
:=
0
;
x
<
255
;
x
++
{
data
:=
getChunk
(
15
,
x
)
f
.
Append
(
uint64
(
x
),
data
)
}
...
...
@@ -74,7 +75,7 @@ func TestFreezerBasics(t *testing.T) {
//db[1] = 010101010101010101010101010101
//db[2] = 020202020202020202020202020202
for
y
:=
byte
(
0
)
;
y
<
255
;
y
++
{
for
y
:=
0
;
y
<
255
;
y
++
{
exp
:=
getChunk
(
15
,
y
)
got
,
err
:=
f
.
Retrieve
(
uint64
(
y
))
if
err
!=
nil
{
...
...
@@ -84,6 +85,11 @@ func TestFreezerBasics(t *testing.T) {
t
.
Fatalf
(
"test %d, got
\n
%x !=
\n
%x"
,
y
,
got
,
exp
)
}
}
// Check that we cannot read too far
_
,
err
=
f
.
Retrieve
(
uint64
(
255
))
if
err
!=
errOutOfBounds
{
t
.
Fatal
(
err
)
}
}
// TestFreezerBasicsClosing tests same as TestFreezerBasics, but also closes and reopens the freezer between
...
...
@@ -102,18 +108,15 @@ func TestFreezerBasicsClosing(t *testing.T) {
t
.
Fatal
(
err
)
}
// Write 15 bytes 255 times, results in 85 files
for
x
:=
byte
(
0
)
;
x
<
255
;
x
++
{
for
x
:=
0
;
x
<
255
;
x
++
{
data
:=
getChunk
(
15
,
x
)
f
.
Append
(
uint64
(
x
),
data
)
f
.
Close
()
f
,
err
=
newCustomTable
(
os
.
TempDir
(),
fname
,
m1
,
m2
,
50
,
true
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
}
defer
f
.
Close
()
for
y
:=
byte
(
0
)
;
y
<
255
;
y
++
{
for
y
:=
0
;
y
<
255
;
y
++
{
exp
:=
getChunk
(
15
,
y
)
got
,
err
:=
f
.
Retrieve
(
uint64
(
y
))
if
err
!=
nil
{
...
...
@@ -142,7 +145,7 @@ func TestFreezerRepairDanglingHead(t *testing.T) {
t
.
Fatal
(
err
)
}
// Write 15 bytes 255 times
for
x
:=
byte
(
0
);
x
<
0xff
;
x
++
{
for
x
:=
0
;
x
<
255
;
x
++
{
data
:=
getChunk
(
15
,
x
)
f
.
Append
(
uint64
(
x
),
data
)
}
...
...
@@ -190,7 +193,7 @@ func TestFreezerRepairDanglingHeadLarge(t *testing.T) {
t
.
Fatal
(
err
)
}
// Write 15 bytes 255 times
for
x
:=
byte
(
0
)
;
x
<
0xff
;
x
++
{
for
x
:=
0
;
x
<
0xff
;
x
++
{
data
:=
getChunk
(
15
,
x
)
f
.
Append
(
uint64
(
x
),
data
)
}
...
...
@@ -223,7 +226,7 @@ func TestFreezerRepairDanglingHeadLarge(t *testing.T) {
t
.
Errorf
(
"Expected error for missing index entry"
)
}
// We should now be able to store items again, from item = 1
for
x
:=
byte
(
1
)
;
x
<
0xff
;
x
++
{
for
x
:=
1
;
x
<
0xff
;
x
++
{
data
:=
getChunk
(
15
,
^
x
)
f
.
Append
(
uint64
(
x
),
data
)
}
...
...
@@ -232,7 +235,7 @@ func TestFreezerRepairDanglingHeadLarge(t *testing.T) {
// And if we open it, we should now be able to read all of them (new values)
{
f
,
_
:=
newCustomTable
(
os
.
TempDir
(),
fname
,
rm
,
wm
,
50
,
true
)
for
y
:=
byte
(
1
)
;
y
<
255
;
y
++
{
for
y
:=
1
;
y
<
255
;
y
++
{
exp
:=
getChunk
(
15
,
^
y
)
got
,
err
:=
f
.
Retrieve
(
uint64
(
y
))
if
err
!=
nil
{
...
...
@@ -257,7 +260,7 @@ func TestSnappyDetection(t *testing.T) {
t
.
Fatal
(
err
)
}
// Write 15 bytes 255 times
for
x
:=
byte
(
0
)
;
x
<
0xff
;
x
++
{
for
x
:=
0
;
x
<
0xff
;
x
++
{
data
:=
getChunk
(
15
,
x
)
f
.
Append
(
uint64
(
x
),
data
)
}
...
...
@@ -308,7 +311,7 @@ func TestFreezerRepairDanglingIndex(t *testing.T) {
t
.
Fatal
(
err
)
}
// Write 15 bytes 9 times : 150 bytes
for
x
:=
byte
(
0
)
;
x
<
9
;
x
++
{
for
x
:=
0
;
x
<
9
;
x
++
{
data
:=
getChunk
(
15
,
x
)
f
.
Append
(
uint64
(
x
),
data
)
}
...
...
@@ -321,7 +324,7 @@ func TestFreezerRepairDanglingIndex(t *testing.T) {
// File sizes should be 45, 45, 45 : items[3, 3, 3)
}
// Crop third file
fileToCrop
:=
filepath
.
Join
(
os
.
TempDir
(),
fmt
.
Sprintf
(
"%s.2.rdat"
,
fname
))
fileToCrop
:=
filepath
.
Join
(
os
.
TempDir
(),
fmt
.
Sprintf
(
"%s.
000
2.rdat"
,
fname
))
// Truncate third file: 45 ,45, 20
{
if
err
:=
assertFileSize
(
fileToCrop
,
45
);
err
!=
nil
{
...
...
@@ -365,7 +368,7 @@ func TestFreezerTruncate(t *testing.T) {
t
.
Fatal
(
err
)
}
// Write 15 bytes 30 times
for
x
:=
byte
(
0
)
;
x
<
30
;
x
++
{
for
x
:=
0
;
x
<
30
;
x
++
{
data
:=
getChunk
(
15
,
x
)
f
.
Append
(
uint64
(
x
),
data
)
}
...
...
@@ -416,7 +419,7 @@ func TestFreezerRepairFirstFile(t *testing.T) {
f
.
Close
()
}
// Truncate the file in half
fileToCrop
:=
filepath
.
Join
(
os
.
TempDir
(),
fmt
.
Sprintf
(
"%s.1.rdat"
,
fname
))
fileToCrop
:=
filepath
.
Join
(
os
.
TempDir
(),
fmt
.
Sprintf
(
"%s.
000
1.rdat"
,
fname
))
{
if
err
:=
assertFileSize
(
fileToCrop
,
40
);
err
!=
nil
{
t
.
Fatal
(
err
)
...
...
@@ -463,7 +466,7 @@ func TestFreezerReadAndTruncate(t *testing.T) {
t
.
Fatal
(
err
)
}
// Write 15 bytes 30 times
for
x
:=
byte
(
0
)
;
x
<
30
;
x
++
{
for
x
:=
0
;
x
<
30
;
x
++
{
data
:=
getChunk
(
15
,
x
)
f
.
Append
(
uint64
(
x
),
data
)
}
...
...
@@ -489,7 +492,7 @@ func TestFreezerReadAndTruncate(t *testing.T) {
// Now, truncate back to zero
f
.
truncate
(
0
)
// Write the data again
for
x
:=
byte
(
0
)
;
x
<
30
;
x
++
{
for
x
:=
0
;
x
<
30
;
x
++
{
data
:=
getChunk
(
15
,
^
x
)
if
err
:=
f
.
Append
(
uint64
(
x
),
data
);
err
!=
nil
{
t
.
Fatalf
(
"error %v"
,
err
)
...
...
@@ -499,6 +502,101 @@ func TestFreezerReadAndTruncate(t *testing.T) {
}
}
func
TestOffset
(
t
*
testing
.
T
)
{
t
.
Parallel
()
wm
,
rm
:=
metrics
.
NewMeter
(),
metrics
.
NewMeter
()
fname
:=
fmt
.
Sprintf
(
"offset-%d"
,
rand
.
Uint64
())
{
// Fill table
f
,
err
:=
newCustomTable
(
os
.
TempDir
(),
fname
,
rm
,
wm
,
40
,
true
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
// Write 6 x 20 bytes, splitting out into three files
f
.
Append
(
0
,
getChunk
(
20
,
0xFF
))
f
.
Append
(
1
,
getChunk
(
20
,
0xEE
))
f
.
Append
(
2
,
getChunk
(
20
,
0xdd
))
f
.
Append
(
3
,
getChunk
(
20
,
0xcc
))
f
.
Append
(
4
,
getChunk
(
20
,
0xbb
))
f
.
Append
(
5
,
getChunk
(
20
,
0xaa
))
f
.
printIndex
()
f
.
Close
()
}
// Now crop it.
{
// delete files 0 and 1
for
i
:=
0
;
i
<
2
;
i
++
{
p
:=
filepath
.
Join
(
os
.
TempDir
(),
fmt
.
Sprintf
(
"%v.%04d.rdat"
,
fname
,
i
))
if
err
:=
os
.
Remove
(
p
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
}
// Read the index file
p
:=
filepath
.
Join
(
os
.
TempDir
(),
fmt
.
Sprintf
(
"%v.ridx"
,
fname
))
indexFile
,
err
:=
os
.
OpenFile
(
p
,
os
.
O_RDWR
,
0644
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
indexBuf
:=
make
([]
byte
,
7
*
indexEntrySize
)
indexFile
.
Read
(
indexBuf
)
// Update the index file, so that we store
// [ file = 2, offset = 4 ] at index zero
tailId
:=
uint32
(
2
)
// First file is 2
itemOffset
:=
uint32
(
4
)
// We have removed four items
zeroIndex
:=
indexEntry
{
offset
:
tailId
,
filenum
:
itemOffset
,
}
buf
:=
zeroIndex
.
marshallBinary
()
// Overwrite index zero
copy
(
indexBuf
,
buf
)
// Remove the four next indices by overwriting
copy
(
indexBuf
[
indexEntrySize
:
],
indexBuf
[
indexEntrySize
*
5
:
])
indexFile
.
WriteAt
(
indexBuf
,
0
)
// Need to truncate the moved index items
indexFile
.
Truncate
(
indexEntrySize
*
(
1
+
2
))
indexFile
.
Close
()
}
// Now open again
{
f
,
err
:=
newCustomTable
(
os
.
TempDir
(),
fname
,
rm
,
wm
,
40
,
true
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
f
.
printIndex
()
// It should allow writing item 6
f
.
Append
(
6
,
getChunk
(
20
,
0x99
))
// It should be fine to fetch 4,5,6
if
got
,
err
:=
f
.
Retrieve
(
4
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
else
if
exp
:=
getChunk
(
20
,
0xbb
);
!
bytes
.
Equal
(
got
,
exp
)
{
t
.
Fatalf
(
"expected %x got %x"
,
exp
,
got
)
}
if
got
,
err
:=
f
.
Retrieve
(
5
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
else
if
exp
:=
getChunk
(
20
,
0xaa
);
!
bytes
.
Equal
(
got
,
exp
)
{
t
.
Fatalf
(
"expected %x got %x"
,
exp
,
got
)
}
if
got
,
err
:=
f
.
Retrieve
(
6
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
else
if
exp
:=
getChunk
(
20
,
0x99
);
!
bytes
.
Equal
(
got
,
exp
)
{
t
.
Fatalf
(
"expected %x got %x"
,
exp
,
got
)
}
// It should error at 0, 1,2,3
for
i
:=
0
;
i
<
4
;
i
++
{
if
_
,
err
:=
f
.
Retrieve
(
uint64
(
i
));
err
==
nil
{
t
.
Fatal
(
"expected err"
)
}
}
}
}
// TODO (?)
// - test that if we remove several head-files, aswell as data last data-file,
// the index is truncated accordingly
...
...
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment