mirror of
https://github.com/databasus/databasus.git
synced 2026-04-06 00:32:03 +02:00
76 lines
2.2 KiB
Go
76 lines
2.2 KiB
Go
package full_backup
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
const defaultWalSegmentSize uint32 = 16 * 1024 * 1024 // 16 MB
|
|
|
|
var (
|
|
startLSNRegex = regexp.MustCompile(`checkpoint redo point at ([0-9A-Fa-f]+/[0-9A-Fa-f]+)`)
|
|
stopLSNRegex = regexp.MustCompile(`write-ahead log end point: ([0-9A-Fa-f]+/[0-9A-Fa-f]+)`)
|
|
)
|
|
|
|
func ParseBasebackupStderr(stderr string) (startSegment, stopSegment string, err error) {
|
|
startMatch := startLSNRegex.FindStringSubmatch(stderr)
|
|
if len(startMatch) < 2 {
|
|
return "", "", fmt.Errorf("failed to parse start WAL location from pg_basebackup stderr")
|
|
}
|
|
|
|
stopMatch := stopLSNRegex.FindStringSubmatch(stderr)
|
|
if len(stopMatch) < 2 {
|
|
return "", "", fmt.Errorf("failed to parse stop WAL location from pg_basebackup stderr")
|
|
}
|
|
|
|
startSegment, err = LSNToSegmentName(startMatch[1], 1, defaultWalSegmentSize)
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("failed to convert start LSN to segment name: %w", err)
|
|
}
|
|
|
|
stopSegment, err = LSNToSegmentName(stopMatch[1], 1, defaultWalSegmentSize)
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("failed to convert stop LSN to segment name: %w", err)
|
|
}
|
|
|
|
return startSegment, stopSegment, nil
|
|
}
|
|
|
|
func LSNToSegmentName(lsn string, timelineID, walSegmentSize uint32) (string, error) {
|
|
high, low, err := parseLSN(lsn)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
segmentsPerXLogID := uint32(0x100000000 / uint64(walSegmentSize))
|
|
logID := high
|
|
segmentOffset := low / walSegmentSize
|
|
|
|
if segmentOffset >= segmentsPerXLogID {
|
|
return "", fmt.Errorf("segment offset %d exceeds segments per XLogId %d", segmentOffset, segmentsPerXLogID)
|
|
}
|
|
|
|
return fmt.Sprintf("%08X%08X%08X", timelineID, logID, segmentOffset), nil
|
|
}
|
|
|
|
func parseLSN(lsn string) (high, low uint32, err error) {
|
|
parts := strings.SplitN(lsn, "/", 2)
|
|
if len(parts) != 2 {
|
|
return 0, 0, fmt.Errorf("invalid LSN format: %q (expected X/Y)", lsn)
|
|
}
|
|
|
|
highVal, err := strconv.ParseUint(parts[0], 16, 32)
|
|
if err != nil {
|
|
return 0, 0, fmt.Errorf("invalid LSN high part %q: %w", parts[0], err)
|
|
}
|
|
|
|
lowVal, err := strconv.ParseUint(parts[1], 16, 32)
|
|
if err != nil {
|
|
return 0, 0, fmt.Errorf("invalid LSN low part %q: %w", parts[1], err)
|
|
}
|
|
|
|
return uint32(highVal), uint32(lowVal), nil
|
|
}
|