2022-02-16 16:16:29 +05:00
package zkv
2022-02-16 16:08:20 +05:00
import (
"bytes"
"encoding/base64"
"fmt"
"io"
"os"
"sync"
"github.com/klauspost/compress/zstd"
)
2022-12-03 20:59:17 +05:00
type Store struct {
2022-02-16 16:08:20 +05:00
dataOffset map [ string ] int64
2022-12-03 20:59:17 +05:00
file * os . File
filePath string
offset int64
encoder * zstd . Encoder
buffer * bytes . Buffer
bufferDataOffset map [ string ] int64
2022-02-16 16:08:20 +05:00
2022-12-03 12:40:36 +05:00
options Options
readOrderChan chan struct { }
2022-12-02 21:37:15 +05:00
mu sync . RWMutex
2022-02-16 16:08:20 +05:00
}
2022-12-03 20:59:17 +05:00
func OpenWithOptions ( filePath string , options Options ) ( * Store , error ) {
options . setDefaults ( )
2022-02-16 16:08:20 +05:00
2022-12-03 20:59:17 +05:00
f , err := os . OpenFile ( filePath , os . O_APPEND | os . O_CREATE | os . O_WRONLY , 0644 )
2022-02-16 16:08:20 +05:00
if err != nil {
2022-12-03 20:59:17 +05:00
f . Close ( )
return nil , fmt . Errorf ( "ошибка при открытии файла для записи: %v" , err )
}
compressor , err := zstd . NewWriter ( f )
if err != nil {
f . Close ( )
return nil , fmt . Errorf ( "ошибка при инициализации компрессора: %v" , err )
}
database := & Store {
dataOffset : make ( map [ string ] int64 ) ,
bufferDataOffset : make ( map [ string ] int64 ) ,
offset : 0 ,
file : f ,
encoder : compressor ,
buffer : new ( bytes . Buffer ) ,
filePath : filePath ,
options : options ,
readOrderChan : make ( chan struct { } , int ( options . MaxParallelReads ) ) }
// restore file data
readF , err := os . Open ( filePath )
if err != nil {
f . Close ( )
return nil , fmt . Errorf ( "ошибка при открытии файла для чтения: %v" , err )
}
defer readF . Close ( )
decompressor , err := zstd . NewReader ( readF )
if err != nil {
f . Close ( )
return nil , fmt . Errorf ( "ошибка при инициализации декомпрессора: %v" , err )
}
defer decompressor . Close ( )
offset := int64 ( 0 )
for {
n , record , err := readRecord ( decompressor )
if err == io . EOF {
break
}
if err != nil {
f . Close ( )
return nil , fmt . Errorf ( "ошибка при чтении записи из файла: %v" , err )
}
switch record . Type {
case RecordTypeSet :
database . dataOffset [ string ( record . KeyHash [ : ] ) ] = offset
case RecordTypeDelete :
delete ( database . dataOffset , string ( record . KeyHash [ : ] ) )
}
offset += n
2022-02-16 16:08:20 +05:00
}
2022-12-03 20:59:17 +05:00
return database , nil
2022-02-16 16:08:20 +05:00
}
2022-12-03 20:59:17 +05:00
func Open ( filePath string ) ( * Store , error ) {
return OpenWithOptions ( filePath , defaultOptions )
}
2022-02-16 16:08:20 +05:00
2022-12-03 20:59:17 +05:00
func ( s * Store ) Set ( key , value interface { } ) error {
s . mu . Lock ( )
defer s . mu . Unlock ( )
return s . set ( key , value )
}
func ( s * Store ) Get ( key , value interface { } ) error {
s . mu . RLock ( )
defer s . mu . RUnlock ( )
return s . get ( key , value )
}
func ( s * Store ) Delete ( key interface { } ) error {
s . mu . Lock ( )
defer s . mu . Unlock ( )
keyHash , err := hashInterface ( key )
2022-02-16 16:08:20 +05:00
if err != nil {
return err
}
2022-12-03 20:59:17 +05:00
record := & Record {
Type : RecordTypeDelete ,
KeyHash : keyHash ,
}
2022-02-16 16:08:20 +05:00
b , err := record . Marshal ( )
if err != nil {
return err
}
2022-12-03 20:59:17 +05:00
delete ( s . dataOffset , string ( record . KeyHash [ : ] ) )
delete ( s . bufferDataOffset , string ( record . KeyHash [ : ] ) )
2022-02-16 16:08:20 +05:00
2022-12-03 20:59:17 +05:00
_ , err = s . buffer . Write ( b )
2022-02-16 16:08:20 +05:00
if err != nil {
return err
}
2022-12-03 20:59:17 +05:00
if s . buffer . Len ( ) > s . options . BufferSize {
err = s . flush ( )
if err != nil {
return err
}
}
2022-02-16 16:08:20 +05:00
return nil
}
2022-12-03 20:59:17 +05:00
func ( s * Store ) Flush ( ) error {
s . mu . Lock ( )
defer s . mu . Unlock ( )
return s . flush ( )
}
2022-02-16 16:08:20 +05:00
2022-12-03 20:59:17 +05:00
func ( s * Store ) Close ( ) error {
s . mu . Lock ( )
defer s . mu . Unlock ( )
2022-12-03 12:40:36 +05:00
2022-12-03 20:59:17 +05:00
err := s . flush ( )
2022-02-16 16:08:20 +05:00
if err != nil {
return err
}
2022-12-03 20:59:17 +05:00
err = s . encoder . Close ( )
2022-02-16 16:08:20 +05:00
if err != nil {
return err
}
2022-12-03 20:59:17 +05:00
return s . file . Close ( )
}
func ( s * Store ) set ( key , value interface { } ) error {
record , err := newRecord ( RecordTypeSet , key , value )
2022-02-16 16:08:20 +05:00
if err != nil {
return err
}
2022-12-03 20:59:17 +05:00
b , err := record . Marshal ( )
2022-02-16 16:08:20 +05:00
if err != nil {
return err
}
2022-12-03 20:59:17 +05:00
s . bufferDataOffset [ string ( record . KeyHash [ : ] ) ] = int64 ( s . buffer . Len ( ) )
_ , err = s . buffer . Write ( b )
2022-02-16 16:08:20 +05:00
if err != nil {
return err
}
2022-12-03 20:59:17 +05:00
if s . buffer . Len ( ) > s . options . BufferSize {
err = s . flush ( )
if err != nil {
return err
}
2022-02-16 16:08:20 +05:00
}
2022-12-03 20:59:17 +05:00
return nil
2022-02-16 16:08:20 +05:00
}
2022-12-03 20:59:17 +05:00
func ( s * Store ) get ( key , value interface { } ) error {
s . readOrderChan <- struct { } { }
defer func ( ) { <- s . readOrderChan } ( )
2022-12-03 12:55:42 +05:00
2022-12-03 20:59:17 +05:00
hashToFind , err := hashInterface ( key )
2022-02-16 16:08:20 +05:00
if err != nil {
2022-12-03 20:59:17 +05:00
return err
2022-02-16 16:08:20 +05:00
}
2022-12-03 20:59:17 +05:00
offset , exists := s . bufferDataOffset [ string ( hashToFind [ : ] ) ]
if exists {
reader := bytes . NewReader ( s . buffer . Bytes ( ) )
err = skip ( reader , offset )
if err != nil {
return err
}
_ , record , err := readRecord ( reader )
if err != nil {
return err
}
return decode ( record . ValueBytes , value )
2022-02-16 16:08:20 +05:00
}
2022-12-03 20:59:17 +05:00
offset , exists = s . dataOffset [ string ( hashToFind [ : ] ) ]
if ! exists {
return ErrNotExists
}
2022-02-16 16:08:20 +05:00
2022-12-03 20:59:17 +05:00
readF , err := os . Open ( s . filePath )
2022-02-16 16:08:20 +05:00
if err != nil {
2022-12-03 20:59:17 +05:00
return err
2022-02-16 16:08:20 +05:00
}
defer readF . Close ( )
decompressor , err := zstd . NewReader ( readF )
if err != nil {
2022-12-03 20:59:17 +05:00
return err
2022-02-16 16:08:20 +05:00
}
defer decompressor . Close ( )
2022-12-03 20:59:17 +05:00
err = skip ( decompressor , offset )
if err != nil {
return err
}
2022-02-16 16:08:20 +05:00
2022-12-03 20:59:17 +05:00
_ , record , err := readRecord ( decompressor )
if err != nil {
return err
2022-02-16 16:08:20 +05:00
}
2022-12-03 20:59:17 +05:00
if ! bytes . Equal ( record . KeyHash [ : ] , hashToFind [ : ] ) {
return fmt . Errorf ( "wrong hash on this offset: expected %s, got %s" , base64 . StdEncoding . EncodeToString ( hashToFind [ : ] ) , base64 . StdEncoding . EncodeToString ( record . KeyHash [ : ] ) ) // TODO: заменить на константную ошибку
}
2022-12-02 20:32:09 +05:00
2022-12-03 20:59:17 +05:00
return decode ( record . ValueBytes , value )
2022-12-03 12:40:36 +05:00
}
2022-12-03 20:59:17 +05:00
func ( s * Store ) flush ( ) error {
l := int64 ( s . buffer . Len ( ) )
2022-12-02 20:32:09 +05:00
2022-12-03 20:59:17 +05:00
_ , err := s . buffer . WriteTo ( s . encoder )
2022-12-02 20:32:09 +05:00
if err != nil {
return err
}
2022-12-03 20:59:17 +05:00
for key , val := range s . bufferDataOffset {
s . dataOffset [ key ] = val + s . offset
2022-12-02 20:32:09 +05:00
}
2022-12-03 20:59:17 +05:00
s . bufferDataOffset = make ( map [ string ] int64 )
2022-12-02 20:32:09 +05:00
2022-12-03 20:59:17 +05:00
s . offset += l
2022-12-02 20:32:09 +05:00
2022-12-03 20:59:17 +05:00
err = s . encoder . Flush ( )
2022-12-02 20:32:09 +05:00
if err != nil {
return err
}
return nil
}