From 583f60da9f9ffd00701a3ebbbbad8ef17f232a90 Mon Sep 17 00:00:00 2001 From: nxshock Date: Sun, 16 Apr 2023 10:32:58 +0500 Subject: [PATCH] Add RebuildIndex() func --- TestDeleteBasic.zkv.idx | Bin 0 -> 3431 bytes TestSmallWrites.zkv.idx | Bin 0 -> 3465 bytes testdata/TestReadBlock.zkv | Bin 0 -> 644 bytes utils.go | 12 +++ zkv.go | 188 ++++++++++++++++++++++++++++--------- zkv_test.go | 55 +++++++++++ 6 files changed, 209 insertions(+), 46 deletions(-) create mode 100644 TestDeleteBasic.zkv.idx create mode 100644 TestSmallWrites.zkv.idx create mode 100644 testdata/TestReadBlock.zkv diff --git a/TestDeleteBasic.zkv.idx b/TestDeleteBasic.zkv.idx new file mode 100644 index 0000000000000000000000000000000000000000..b341db72544cb67bd4fd560f43e99a37bd67a577 GIT binary patch literal 3431 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 F@IM0uF`xhd literal 0 HcmV?d00001 diff --git a/TestSmallWrites.zkv.idx b/TestSmallWrites.zkv.idx new file mode 100644 index 0000000000000000000000000000000000000000..6de70daa2dcb36824aeffb528ea00ae58a9cd592 GIT binary patch literal 3465 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 literal 0 HcmV?d00001 diff --git a/testdata/TestReadBlock.zkv b/testdata/TestReadBlock.zkv new file mode 100644 index 0000000000000000000000000000000000000000..7916c5b25ea46be9f90ae34ec57b2de949cc318b GIT binary patch literal 644 zcmci8y-Px26vy%B(TAH!Iu&KPINUM_gbH7%gc>9e9IRY4$n1hanXlk&({wNil8|U= ziIz;l!6695t%jf_+FS~1h+2e7h+mhh|AL+MoC6=e#;WO;krPJpq&+sIl$3IAA+QtO zphy#{q--5{#cMAL#W#0-%1DzVqAz(we+mR9;LL`J84}_Y=+`i`G})d zLd-}@8$Axu=vB86fIB`4l2I1bLwQKn=>LHNgj!6^H3jO4oL zo9_5B$e?aeAdjbQJfJ+h((glsS;kGtAyo0=ap6Gckadjo&D3md-T&0%MZ3d=OKuDm zoZ$k`XjmeFC~_KPZIbR9`#4=gc~JIo#I3TAwtaVu6uMY%wo|Xh;}f^YPM{`n%4p$A fkJkuVW)w$IK*@~z-6?$G9UrL6N) 0 { - idxBuf := new(bytes.Buffer) - - err = gob.NewEncoder(idxBuf).Encode(s.dataOffset) - if err != nil { - return err - } - - err = os.WriteFile(s.filePath+indexFileExt, idxBuf.Bytes(), 0644) + err = s.saveIndex() if err != nil { return err } @@ -410,3 +383,126 @@ func (s *Store) flush() error { return nil } + +func readBlock(r *bufio.Reader) (line []byte, n int, err error) { + delim := []byte{0x28, 0xb5, 0x2f, 0xfd} + + line = make([]byte, len(delim)) + copy(line, delim) + + for { + s, err := r.ReadBytes(delim[len(delim)-1]) + line = append(line, []byte(s)...) + if err != nil { + if bytes.Equal(line, delim) { // contains only magic number + return []byte{}, 0, err + } else { + return line, len(s), err + } + } + + if bytes.Equal(line, append(delim, delim...)) { // first block + line = make([]byte, len(delim)) + copy(line, delim) + continue + } + + if bytes.HasSuffix(line, delim) { + return line[:len(line)-len(delim)], len(s), nil + } + } +} + +// RebuildIndex renews index from store file +func (s *Store) RebuildIndex() error { + s.mu.Lock() + defer s.mu.Unlock() + + err := s.rebuildIndex() + if err != nil { + return err + } + + if s.options.useIndexFile { + return s.saveIndex() + } + + return nil +} + +func (s *Store) rebuildIndex() error { + f, err := os.Open(s.filePath) + if err != nil { + return err + } + defer f.Close() + + r := bufio.NewReader(f) + + var blockOffset int64 + + s.dataOffset = make(map[string]Offsets) + + for { + l, n, err := readBlock(r) + if err != nil { + if err != io.EOF { + return err + } else if err == io.EOF && len(l) == 0 { + break + } + } + + dec, err := zstd.NewReader(bytes.NewReader(l)) + + var recordOffset int64 + for { + n, record, err := readRecord(dec) + if err != nil { + if err == io.EOF { + break + } else { + return err + } + } + + switch record.Type { + case RecordTypeSet: + s.dataOffset[string(record.KeyHash[:])] = Offsets{BlockOffset: blockOffset, RecordOffset: recordOffset} + case RecordTypeDelete: + delete(s.dataOffset, string(record.KeyHash[:])) + } + recordOffset += n + } + + blockOffset += int64(n) + } + + idxBuf := new(bytes.Buffer) + + err = gob.NewEncoder(idxBuf).Encode(s.dataOffset) + if err != nil { + return err + } + + err = os.WriteFile(s.filePath+indexFileExt, idxBuf.Bytes(), 0644) + if err != nil { + return err + } + + return nil +} + +func (s *Store) saveIndex() error { + f, err := os.OpenFile(s.filePath+indexFileExt, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return err + } + + err = gob.NewEncoder(f).Encode(s.dataOffset) + if err != nil { + return err + } + + return f.Close() +} diff --git a/zkv_test.go b/zkv_test.go index 5229178..ec2d2de 100644 --- a/zkv_test.go +++ b/zkv_test.go @@ -1,6 +1,8 @@ package zkv import ( + "bufio" + "io" "os" "testing" @@ -355,3 +357,56 @@ func TestIndexFileBasic(t *testing.T) { err = db.Close() assert.NoError(t, err) } + +func TestReadBlock(t *testing.T) { + file, err := os.Open("testdata/TestReadBlock.zkv") + assert.NoError(t, err) + defer file.Close() + + r := bufio.NewReader(file) + + line, _, err := readBlock(r) + assert.Equal(t, []byte{0x28, 0xb5, 0x2f, 0xfd, 0x04, 0x00, 0x99, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xff, 0x81, 0x03, 0x01, 0x01, 0x06, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x01, 0xff, 0x82, 0x00, 0x01, 0x03, 0x01, 0x04, 0x54, 0x79, 0x70, 0x65, 0x01, 0x06, 0x00, 0x01, 0x07, 0x4b, 0x65, 0x79, 0x48, 0x61, 0x73, 0x68, 0x01, 0xff, 0x84, 0x00, 0x01, 0x0a, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x19, 0xff, 0x83, 0x01, 0x01, 0x01, 0x09, 0x5b, 0x32, 0x38, 0x5d, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x01, 0xff, 0x84, 0x00, 0x01, 0x06, 0x01, 0x38, 0x00, 0x00, 0x36, 0xff, 0x82, 0x01, 0x01, 0x01, 0x1c, 0xff, 0x90, 0xff, 0xf4, 0x25, 0x15, 0x70, 0x75, 0x5c, 0xff, 0xf4, 0xff, 0xbc, 0xff, 0xf9, 0xff, 0xde, 0xff, 0x93, 0xff, 0xf8, 0x0d, 0x0e, 0x78, 0x5b, 0xff, 0x81, 0xff, 0x95, 0x6e, 0xff, 0xab, 0x4b, 0xff, 0xe8, 0x37, 0xff, 0x97, 0x68, 0x41, 0x3d, 0x01, 0x04, 0x03, 0x04, 0x00, 0x02, 0x00, 0x25, 0xd5, 0x63, 0x21}, line) + line, _, err = readBlock(r) + assert.Equal(t, []byte{0x28, 0xb5, 0x2f, 0xfd, 0x04, 0x00, 0x89, 0x04, 0x00, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xff, 0x81, 0x03, 0x01, 0x01, 0x06, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x01, 0xff, 0x82, 0x00, 0x01, 0x03, 0x01, 0x04, 0x54, 0x79, 0x70, 0x65, 0x01, 0x06, 0x00, 0x01, 0x07, 0x4b, 0x65, 0x79, 0x48, 0x61, 0x73, 0x68, 0x01, 0xff, 0x84, 0x00, 0x01, 0x0a, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x19, 0xff, 0x83, 0x01, 0x01, 0x01, 0x09, 0x5b, 0x32, 0x38, 0x5d, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x01, 0xff, 0x84, 0x00, 0x01, 0x06, 0x01, 0x38, 0x00, 0x00, 0x34, 0xff, 0x82, 0x01, 0x01, 0x01, 0x1c, 0xff, 0x84, 0xff, 0x84, 0xff, 0xc1, 0x21, 0x02, 0xff, 0x8b, 0xff, 0xd7, 0x6d, 0xff, 0xd0, 0xff, 0xad, 0x1a, 0x55, 0x14, 0x5c, 0xff, 0xb1, 0x04, 0x37, 0x29, 0x2f, 0x78, 0x18, 0xff, 0xb5, 0xff, 0xe4, 0x56, 0x4e, 0xff, 0x8d, 0x19, 0x46, 0x01, 0x04, 0x03, 0x04, 0x00, 0x04, 0x00, 0x0c, 0x3b, 0xbf, 0x39}, line) + line, _, err = readBlock(r) + assert.Equal(t, []byte{0x28, 0xb5, 0x2f, 0xfd, 0x04, 0x00, 0x99, 0x04, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xff, 0x81, 0x03, 0x01, 0x01, 0x06, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x01, 0xff, 0x82, 0x00, 0x01, 0x03, 0x01, 0x04, 0x54, 0x79, 0x70, 0x65, 0x01, 0x06, 0x00, 0x01, 0x07, 0x4b, 0x65, 0x79, 0x48, 0x61, 0x73, 0x68, 0x01, 0xff, 0x84, 0x00, 0x01, 0x0a, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x19, 0xff, 0x83, 0x01, 0x01, 0x01, 0x09, 0x5b, 0x32, 0x38, 0x5d, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x01, 0xff, 0x84, 0x00, 0x01, 0x06, 0x01, 0x38, 0x00, 0x00, 0x36, 0xff, 0x82, 0x01, 0x01, 0x01, 0x1c, 0x25, 0x79, 0x3e, 0x46, 0x4e, 0xff, 0xac, 0x06, 0x27, 0xff, 0xb1, 0xff, 0xa3, 0xff, 0xaa, 0xff, 0xe3, 0xff, 0xde, 0x37, 0x71, 0x63, 0x72, 0xff, 0x89, 0x0d, 0xff, 0x85, 0x39, 0xff, 0xb5, 0xff, 0xb9, 0xff, 0x8a, 0xff, 0x9e, 0x60, 0xff, 0xad, 0x17, 0x01, 0x04, 0x03, 0x04, 0x00, 0x06, 0x00, 0x52, 0x08, 0x3e, 0x26}, line) + line, _, err = readBlock(r) + assert.Equal(t, []byte{0x28, 0xb5, 0x2f, 0xfd, 0x04, 0x00, 0xc9, 0x04, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xff, 0x81, 0x03, 0x01, 0x01, 0x06, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x01, 0xff, 0x82, 0x00, 0x01, 0x03, 0x01, 0x04, 0x54, 0x79, 0x70, 0x65, 0x01, 0x06, 0x00, 0x01, 0x07, 0x4b, 0x65, 0x79, 0x48, 0x61, 0x73, 0x68, 0x01, 0xff, 0x84, 0x00, 0x01, 0x0a, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x19, 0xff, 0x83, 0x01, 0x01, 0x01, 0x09, 0x5b, 0x32, 0x38, 0x5d, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x01, 0xff, 0x84, 0x00, 0x01, 0x06, 0x01, 0x38, 0x00, 0x00, 0x3c, 0xff, 0x82, 0x01, 0x01, 0x01, 0x1c, 0xff, 0xbf, 0x25, 0xff, 0xef, 0xff, 0xc8, 0xff, 0x85, 0x2c, 0xff, 0xbf, 0xff, 0xb5, 0xff, 0xad, 0xff, 0xfa, 0xff, 0xaf, 0x1c, 0xff, 0xe7, 0x71, 0xff, 0xfa, 0x36, 0xff, 0x95, 0x1b, 0xff, 0x91, 0xff, 0xab, 0x36, 0xff, 0xcd, 0x7a, 0x33, 0xff, 0xf7, 0xff, 0xec, 0xff, 0xee, 0xff, 0xc1, 0x01, 0x04, 0x03, 0x04, 0x00, 0x08, 0x00, 0xa5, 0x0e, 0x62, 0x53}, line) + + line, _, err = readBlock(r) + assert.Equal(t, line, []byte{}) + assert.Equal(t, io.EOF, err) +} + +func TestRebuildIndex(t *testing.T) { + const filePath = "TestRebuiltIndex.zkv" + const recordCount = 4 + defer os.Remove(filePath) + defer os.Remove(filePath + indexFileExt) + + for i := 1; i <= recordCount; i++ { + db, err := Open(filePath) + assert.NoError(t, err) + + err = db.Set(i, i) + assert.NoError(t, err) + + err = db.Close() + assert.NoError(t, err) + } + + db, err := Open(filePath) + assert.NoError(t, err) + + err = db.RebuildIndex() + assert.NoError(t, err) + + for i := 1; i <= recordCount; i++ { + var gotValue int + + err = db.Get(i, &gotValue) + assert.NoError(t, err) + assert.Equal(t, i, gotValue) + } +}