145 lines
4.1 KiB
Go
145 lines
4.1 KiB
Go
package replication
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/KevoDB/kevo/pkg/common/log"
|
|
"github.com/KevoDB/kevo/pkg/engine/interfaces"
|
|
"github.com/KevoDB/kevo/pkg/wal"
|
|
)
|
|
|
|
// EngineApplier implements the WALEntryApplier interface for applying
|
|
// WAL entries to a database engine.
|
|
type EngineApplier struct {
|
|
engine interfaces.Engine
|
|
}
|
|
|
|
// NewEngineApplier creates a new engine applier
|
|
func NewEngineApplier(engine interfaces.Engine) *EngineApplier {
|
|
return &EngineApplier{
|
|
engine: engine,
|
|
}
|
|
}
|
|
|
|
// Apply applies a WAL entry to the engine through its API
|
|
// This bypasses the read-only check for replication purposes
|
|
func (e *EngineApplier) Apply(entry *wal.Entry) error {
|
|
log.Info("Replica applying WAL entry through engine API: seq=%d, type=%d, key=%s",
|
|
entry.SequenceNumber, entry.Type, string(entry.Key))
|
|
|
|
// Check if engine is in read-only mode
|
|
isReadOnly := false
|
|
if checker, ok := e.engine.(interface{ IsReadOnly() bool }); ok {
|
|
isReadOnly = checker.IsReadOnly()
|
|
}
|
|
|
|
// Handle application based on read-only status and operation type
|
|
if isReadOnly {
|
|
return e.applyInReadOnlyMode(entry)
|
|
}
|
|
|
|
return e.applyInNormalMode(entry)
|
|
}
|
|
|
|
// applyInReadOnlyMode applies a WAL entry in read-only mode
|
|
func (e *EngineApplier) applyInReadOnlyMode(entry *wal.Entry) error {
|
|
log.Info("Applying entry in read-only mode: seq=%d", entry.SequenceNumber)
|
|
|
|
switch entry.Type {
|
|
case wal.OpTypePut:
|
|
// Try internal interface first
|
|
if putter, ok := e.engine.(interface{ PutInternal(key, value []byte) error }); ok {
|
|
return putter.PutInternal(entry.Key, entry.Value)
|
|
}
|
|
|
|
// Try temporarily disabling read-only mode
|
|
if setter, ok := e.engine.(interface{ SetReadOnly(bool) }); ok {
|
|
setter.SetReadOnly(false)
|
|
err := e.engine.Put(entry.Key, entry.Value)
|
|
setter.SetReadOnly(true)
|
|
return err
|
|
}
|
|
|
|
// Fall back to normal operation which may fail
|
|
return e.engine.Put(entry.Key, entry.Value)
|
|
|
|
case wal.OpTypeDelete:
|
|
// Try internal interface first
|
|
if deleter, ok := e.engine.(interface{ DeleteInternal(key []byte) error }); ok {
|
|
return deleter.DeleteInternal(entry.Key)
|
|
}
|
|
|
|
// Try temporarily disabling read-only mode
|
|
if setter, ok := e.engine.(interface{ SetReadOnly(bool) }); ok {
|
|
setter.SetReadOnly(false)
|
|
err := e.engine.Delete(entry.Key)
|
|
setter.SetReadOnly(true)
|
|
return err
|
|
}
|
|
|
|
// Fall back to normal operation which may fail
|
|
return e.engine.Delete(entry.Key)
|
|
|
|
case wal.OpTypeBatch:
|
|
// Try internal interface first
|
|
if batcher, ok := e.engine.(interface {
|
|
ApplyBatchInternal(entries []*wal.Entry) error
|
|
}); ok {
|
|
return batcher.ApplyBatchInternal([]*wal.Entry{entry})
|
|
}
|
|
|
|
// Try temporarily disabling read-only mode
|
|
if setter, ok := e.engine.(interface{ SetReadOnly(bool) }); ok {
|
|
setter.SetReadOnly(false)
|
|
err := e.engine.ApplyBatch([]*wal.Entry{entry})
|
|
setter.SetReadOnly(true)
|
|
return err
|
|
}
|
|
|
|
// Fall back to normal operation which may fail
|
|
return e.engine.ApplyBatch([]*wal.Entry{entry})
|
|
|
|
case wal.OpTypeMerge:
|
|
// Handle merge as a put operation for compatibility
|
|
if setter, ok := e.engine.(interface{ SetReadOnly(bool) }); ok {
|
|
setter.SetReadOnly(false)
|
|
err := e.engine.Put(entry.Key, entry.Value)
|
|
setter.SetReadOnly(true)
|
|
return err
|
|
}
|
|
return e.engine.Put(entry.Key, entry.Value)
|
|
|
|
default:
|
|
return fmt.Errorf("unsupported WAL entry type: %d", entry.Type)
|
|
}
|
|
}
|
|
|
|
// applyInNormalMode applies a WAL entry in normal mode
|
|
func (e *EngineApplier) applyInNormalMode(entry *wal.Entry) error {
|
|
log.Info("Applying entry in normal mode: seq=%d", entry.SequenceNumber)
|
|
|
|
switch entry.Type {
|
|
case wal.OpTypePut:
|
|
return e.engine.Put(entry.Key, entry.Value)
|
|
|
|
case wal.OpTypeDelete:
|
|
return e.engine.Delete(entry.Key)
|
|
|
|
case wal.OpTypeBatch:
|
|
return e.engine.ApplyBatch([]*wal.Entry{entry})
|
|
|
|
case wal.OpTypeMerge:
|
|
// Handle merge as a put operation for compatibility
|
|
return e.engine.Put(entry.Key, entry.Value)
|
|
|
|
default:
|
|
return fmt.Errorf("unsupported WAL entry type: %d", entry.Type)
|
|
}
|
|
}
|
|
|
|
// Sync ensures all applied entries are persisted
|
|
func (e *EngineApplier) Sync() error {
|
|
// Force a flush of in-memory tables to ensure durability
|
|
return e.engine.FlushImMemTables()
|
|
}
|