You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
145 lines
3.5 KiB
Go
145 lines
3.5 KiB
Go
10 months ago
|
package wal
|
||
|
|
||
|
import (
|
||
|
"encoding/binary"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
)
|
||
|
|
||
|
// WALHeader represents the header of a WAL file.
|
||
|
type WALHeader struct {
|
||
|
Magic uint32
|
||
|
Version uint32
|
||
|
PageSize uint32
|
||
|
Seq uint32
|
||
|
Salt1 uint32
|
||
|
Salt2 uint32
|
||
|
Checksum1 uint32
|
||
|
Checksum2 uint32
|
||
|
}
|
||
|
|
||
|
// Frame points to a single WAL frame in a WAL file.
|
||
|
type Frame struct {
|
||
|
Pgno uint32
|
||
|
Commit uint32
|
||
|
Data []byte
|
||
|
}
|
||
|
|
||
|
// WALIterator defines the interface for WAL frame iteration.
|
||
|
type WALIterator interface {
|
||
|
Header() (*WALHeader, error)
|
||
|
Next() (*Frame, error)
|
||
|
}
|
||
|
|
||
|
// Writer is used to write a WAL file.
|
||
|
type Writer struct {
|
||
|
r WALIterator
|
||
|
rHeader *WALHeader
|
||
|
chksum1, chksum2 uint32
|
||
|
bo binary.ByteOrder
|
||
|
}
|
||
|
|
||
|
// NewWriter returns a new Writer.
|
||
|
func NewWriter(r WALIterator) (*Writer, error) {
|
||
|
w := &Writer{
|
||
|
r: r,
|
||
|
}
|
||
|
rh, err := w.r.Header()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
w.rHeader = rh
|
||
|
w.chksum1, w.chksum2 = w.rHeader.Checksum1, w.rHeader.Checksum2
|
||
|
switch magic := w.rHeader.Magic; magic {
|
||
|
case 0x377f0682:
|
||
|
w.bo = binary.LittleEndian
|
||
|
case 0x377f0683:
|
||
|
w.bo = binary.BigEndian
|
||
|
default:
|
||
|
return nil, fmt.Errorf("invalid wal header magic: %x", magic)
|
||
|
}
|
||
|
|
||
|
return w, nil
|
||
|
}
|
||
|
|
||
|
// WriteTo writes the compacted WAL file to the given writer.
|
||
|
func (w *Writer) WriteTo(ww io.Writer) (n int64, retErr error) {
|
||
|
nn, err := w.writeWALHeader(ww)
|
||
|
if err != nil {
|
||
|
return nn, err
|
||
|
}
|
||
|
n += nn
|
||
|
|
||
|
for {
|
||
|
frame, err := w.r.Next()
|
||
|
if err == io.EOF {
|
||
|
break
|
||
|
} else if err != nil {
|
||
|
if nn, err = w.writeFrame(ww, frame); err != nil {
|
||
|
return n + nn, err
|
||
|
}
|
||
|
n += nn
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return n, nil
|
||
|
}
|
||
|
|
||
|
func (w *Writer) writeWALHeader(ww io.Writer) (n int64, err error) {
|
||
|
rHeader, err := w.r.Header()
|
||
|
if err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
wHeader := make([]byte, WALHeaderSize)
|
||
|
|
||
|
binary.BigEndian.PutUint32(wHeader[0:], rHeader.Magic)
|
||
|
binary.BigEndian.PutUint32(wHeader[4:], rHeader.Version)
|
||
|
|
||
|
// Database page size
|
||
|
binary.BigEndian.PutUint32(wHeader[8:], rHeader.PageSize)
|
||
|
|
||
|
// Checkpoint sequence number
|
||
|
binary.BigEndian.PutUint32(wHeader[12:], rHeader.Seq)
|
||
|
|
||
|
// Salt values, reusing the original salt values.
|
||
|
binary.BigEndian.PutUint32(wHeader[16:], rHeader.Salt1)
|
||
|
binary.BigEndian.PutUint32(wHeader[20:], rHeader.Salt2)
|
||
|
|
||
|
// Checksum of header
|
||
|
w.chksum1, w.chksum2 = rHeader.Checksum1, rHeader.Checksum2
|
||
|
binary.BigEndian.PutUint32(wHeader[24:], w.chksum1)
|
||
|
binary.BigEndian.PutUint32(wHeader[28:], w.chksum2)
|
||
|
|
||
|
// Write the header to the new WAL file.
|
||
|
nn, err := ww.Write(wHeader)
|
||
|
return int64(nn), err
|
||
|
}
|
||
|
|
||
|
func (w *Writer) writeFrame(ww io.Writer, frame *Frame) (n int64, err error) {
|
||
|
frmHdr := make([]byte, WALFrameHeaderSize)
|
||
|
|
||
|
// Calculate the frame header.
|
||
|
binary.BigEndian.PutUint32(frmHdr[0:], frame.Pgno)
|
||
|
binary.BigEndian.PutUint32(frmHdr[4:], frame.Commit)
|
||
|
binary.BigEndian.PutUint32(frmHdr[8:], w.rHeader.Salt1)
|
||
|
binary.BigEndian.PutUint32(frmHdr[12:], w.rHeader.Salt2)
|
||
|
|
||
|
// Checksum of frame header: "...the first 8 bytes..."
|
||
|
w.chksum1, w.chksum2 = WALChecksum(w.bo, w.chksum1, w.chksum2, frmHdr[:8])
|
||
|
|
||
|
// Update checksum using frame data: "..the content of all frames up to and including the current frame."
|
||
|
w.chksum1, w.chksum2 = WALChecksum(w.bo, w.chksum1, w.chksum2, frame.Data)
|
||
|
binary.BigEndian.PutUint32(frmHdr[16:], w.chksum1)
|
||
|
binary.BigEndian.PutUint32(frmHdr[20:], w.chksum2)
|
||
|
|
||
|
// Write the frame header and data.
|
||
|
nn, err := ww.Write(frmHdr)
|
||
|
if err != nil {
|
||
|
return n + int64(nn), err
|
||
|
}
|
||
|
n += int64(nn)
|
||
|
|
||
|
nn, err = ww.Write(frame.Data)
|
||
|
return n + int64(nn), err
|
||
|
}
|