Conversation
🔴 Performance DegradationSome benchmarks have degraded compared to the previous run. Show table
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #334 +/- ##
==========================================
- Coverage 71.65% 71.33% -0.32%
==========================================
Files 204 209 +5
Lines 14770 15208 +438
==========================================
+ Hits 10583 10849 +266
- Misses 3435 3572 +137
- Partials 752 787 +35 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
@seqbenchbot up main bulk |
|
@seqbenchbot down 0c97854c |
|
Nice, @cheb0 Your request was successfully served. Have a great time! |
storage/meta_block.go
Outdated
| // MetaBlock format: M : V : C : LLLL : UUUU : PPPP : DDDD-DDDD : HHHH | ||
| // M = Magic (101), V = Version, C = Codec, L = Length, U = Raw Length, P = Payload Checksum, D = Docs Offset, H = Header Checksum | ||
|
|
||
| type MetaBlock []byte |
storage/wal_writer.go
Outdated
| // BlockAlignment is the alignment boundary for blocks in the new WAL format. Must be greater than | ||
| // MetaBlock header (27 bytes) to prevent header torn writes and allow faster navigation during replay | ||
| // of corrupted WAL file | ||
| BlockAlignment int64 = 64 |
There was a problem hiding this comment.
| BlockAlignment int64 = 64 | |
| WALBlockAlignment int64 = 64 |
| "github.com/ozontech/seq-db/logger" | ||
| ) | ||
|
|
||
| type WalRecord struct { |
There was a problem hiding this comment.
Let's name everything WAL-related consistently. Somewhere you have Wal, somewhere you have WAL.
storage/wal_reader.go
Outdated
| } | ||
|
|
||
| for { | ||
| headerBuf := make([]byte, MetaBlockHeaderLen) |
There was a problem hiding this comment.
Seems like we could reuse headerBuf on each iteration.
Is it possible to move headerBuf outside of the loop?
| "github.com/ozontech/seq-db/metric/stopwatch" | ||
| ) | ||
|
|
||
| const ( |
There was a problem hiding this comment.
Maybe we can move these variables to meta_block.go (which must be renamed to wal_block.go in my opinion) and introduce new structure WALHeader with different access methods e.g. Magic() and Version()?
There was a problem hiding this comment.
The initial version had exactly that - a dedicated WalHeader struct. But I didn't see much of the point right now since there is no much usage of it.
I wouldn't move constants to wal_block.go since they are not related to the block itself, only to WAL.
There was a problem hiding this comment.
Yeah, I see, then a small nit:
const (
// _WalVersionInitial is the first version of WAL file
// with CRC32 checksums and 64 byte alignment for blocks.
_WalVersionInitial byte = iota
)
const (
// WalMagic is the magic number at the start of WAL files.
WalMagic uint32 = 0xFFFFFFFF
// WalCurrentVersion is the current WAL format version.
WalCurrentVersion = _WalVersionInitial
// WalHeaderSize is the size of the WAL header in bytes (4 bytes magic + 1 byte version).
// 59 bytes are also reserved due to alignment.
WalHeaderSize = 5
)
const (
// WalBlockAlignment is the alignment boundary for blocks in the new WAL format.
// Must be greater than [WalBlockHeaderLen] to prevent header torn writes
// and allow faster navigation during replay of corrupted WAL file.
WalBlockAlignment int64 = 64
)That's just my personal preference so you can ignore it (and it's very insignificant, I guess). I've added prefix (_) to _WalVersionInitial to send a signal to other developers that this variable should not be used.
By the way, you use specific WAL version instead of current WAL version in wal_reader.go:
version := header[4]
if version != _WalVersionInitial {
return nil, fmt.Errorf("unknown WAL version: %d (supported: %d)", version, _WalVersionInitial)
}
storage/wal_writer.go
Outdated
| // WALMagic is the magic number at the start of WAL files | ||
| WALMagic uint32 = 0xFFFFFFFF | ||
| // WALVersion1 is the first version of WAL file with CRC32 checksums and 64 byte alignment for blocks. | ||
| WALVersion1 uint8 = 1 |
There was a problem hiding this comment.
Why WALVersion1 and not just WALVersion?
There was a problem hiding this comment.
Next line, WALCurrentVersion = WalVersion1
| } | ||
| } | ||
| return result | ||
| } |
There was a problem hiding this comment.
I guess we can get rid of CAS loop where.
Let's rewrite initialization like this:
func NewWalWriter(ws WriteSyncer, offset int64, skipSync bool) *WalWriter {
w := &WalWriter{
ws: ws,
skipSync: skipSync,
notify: make(chan struct{}, 1),
}
// Here we have offset that can be calculated like 64 * `k` for some `k`.
w.offset.Store(nextBlockOffset(offset))
// write a header at the beggining if it's a new file
if offset == 0 {
if err := writeWALHeader(ws); err != nil {
logger.Panic("failed to write WAL header", zap.Error(err))
}
if !skipSync {
_ = ws.Sync()
}
// Here we have offset that can be calculated like 64 * `k` for some `k`.
// In this case `k` is equal to 1.
w.offset.Store(nextBlockOffset(WALHeaderSize))
}
w.wg.Add(1)
go func() {
w.syncLoop()
w.wg.Done()
}()
return w
}So we know that offset is already aligned.
Now we can reserve space like this:
func (w *WalWriter) reserveSpace(blockSize int64) int64 {
aligned := nextBlockOffset(blockSize)
// w.offset is already aligned.
// So when we add aligned block we still have aligned offset.
end := w.offset.Add(aligned)
start := end - aligned
return start
}I've applied these changes locally and tests are green.
There was a problem hiding this comment.
yep, if offset is always aligned, then CAS is not needed. fixed
frac/active.go
Outdated
| legacyMetaFileName := baseFileName + consts.MetaFileSuffix | ||
| if _, err := os.Stat(legacyMetaFileName); err == nil { | ||
| // .meta file exists | ||
| metaFile, metaStats = mustOpenFile(legacyMetaFileName, config.SkipFsync) | ||
| metaSize = uint64(metaStats.Size()) | ||
| metaReader = storage.NewDocBlocksReader(readLimiter, metaFile) | ||
| writer = NewActiveWriterLegacy(docsFile, metaFile, docsStats.Size(), metaStats.Size(), config.SkipFsync) | ||
| logger.Info("using legacy meta file format", zap.String("fraction", baseFileName)) | ||
| } else { | ||
| logger.Info("using new WAL format", zap.String("fraction", baseFileName)) | ||
| walFileName := baseFileName + consts.WalFileSuffix | ||
| metaFile, metaStats = mustOpenFile(walFileName, config.SkipFsync) | ||
| metaSize = uint64(metaStats.Size()) | ||
| writer = NewActiveWriter(docsFile, metaFile, docsStats.Size(), metaStats.Size(), config.SkipFsync) | ||
| var err error | ||
| walReader, err = storage.NewWalReader(readLimiter, metaFile, baseFileName) | ||
| if err != nil { | ||
| logger.Fatal("failed to initialize WAL reader", zap.String("fraction", baseFileName), zap.Error(err)) | ||
| } | ||
| } |
There was a problem hiding this comment.
Maybe we can move this to separate method?
storage/wal_writer.go
Outdated
| // WalWriter writes MetaBlocks to a WAL file with header and 64-byte alignment. | ||
| // Format: [Header 5B] [... -> align to 64] [MetaBlock] [... -> align to 64] [MetaBlock] ... | ||
| type WalWriter struct { | ||
| ws WriteSyncer |
There was a problem hiding this comment.
Why not reusing frac.FileWriter here? Yeah, I've noticed that in FileWriter there is a conversion from new WAL format to old Meta format but I guess it can be done somewhere else...
I do not like the duplication of fsync-coalescing logic here.
There was a problem hiding this comment.
That's a fair point. I redesigned a bit - FileWriter now (probably should be named SyncFileWriter) is not aware of any block formats, while convertions of doc => wal happens in a separate wrapper LegacyMetaWriter.
WalWriter now also works on top of FileWriter, which means it now reserves space from FileWriter too, since offset is owned by FileWriter.
Both WalWriter and LegacyMetaWriter implement a trait MetaWriter.
I also moved FileWriter to storage package to prevent circular dependency.
There was a problem hiding this comment.
Both
WalWriterandLegacyMetaWriterimplement a trait
I guess, you've confused languages a little bit 🤣
Overall, looks great.
🔴 Performance DegradationSome benchmarks have degraded compared to the previous run. Show table
|
🔴 Performance DegradationSome benchmarks have degraded compared to the previous run. Show table
|
|
@seqbenchbot up main bulk |
|
@seqbenchbot down b9ff764f |
|
Nice, @cheb0 The benchmark with identificator Show summary
Have a great time! |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Description
A new WAL file which can withstand arbitrary failures like power shutdowns, lost disk sector writes or corrupted bytes.
There is also a new
MetaBlocktype which replacesDocBlockat all places where meta is transfered or stored to filesystem.Fixes #311