Class FallbackKVDatabase
- All Implemented Interfaces:
KVDatabase
KVDatabase
that automatically migrates between a clustered RaftKVDatabase
and a local, non-clustered "standalone mode" KVDatabase
, based on availability of the Raft cluster.
A RaftKVDatabase
requires that the local node be part of a cluster majority, otherwise, transactions
cannot commit (even read-only ones) and application progress halts. This class adds partition tolerance to
a RaftKVDatabase
, by maintaining a separate private "standalone mode" KVDatabase
that can be used
in lieu of the normal RaftKVDatabase
when the Raft cluster is unavailable.
Instances transparently and automatically switch over to standalone mode KVDatabase
when they determine
that the RaftKVDatabase
is unavailable, and automatically switch back to using the RaftKVDatabase
once it is available again. The rate of switching is limited by an enforced hysteresis; see
FallbackTarget.getMinAvailableTime()
and FallbackTarget.getMinUnavailableTime()
.
Of course, this sacrifices consistency. To address that, a configurable MergeStrategy
is used to migrate the data
when switching between normal mode and standalone mode. The MergeStrategy
is given read-only access to the
database being switched away from, and read-write access to the database being switched to; when switching away from
the RaftKVDatabase
, Consistency.EVENTUAL_COMMITTED
is used to eliminate the requirement for
communication with the rest of the cluster.
Although more exotic, instances support migrating between multiple RaftKVDatabase
s in a prioritized list.
For example, the local node may be part of two independent RaftKVDatabase
clusters: a higher priority one
containing every node, and a lower priority one containing only nodes in the same data center as the local node.
In any case, the "standalone mode" database (which is not a clustered database) always has the lowest priority.
Raft cluster availability is determined by FallbackTarget.checkAvailability(io.permazen.kv.raft.fallback.FallbackKVDatabase)
; subclasses may override the default
implementation if desired.
-
Field Summary
-
Constructor Summary
-
Method Summary
Modifier and TypeMethodDescriptionprotected RaftKVTransaction
Create a Raft availability check transaction.protected RaftKVTransaction
Create a Raft destination transaction.protected Thread
createExecutorThread
(Runnable action, int uniqueId) Create the service thread used by this instance.protected RaftKVTransaction
Create a Raft source transaction.Create a new transaction.createTransaction
(Map<String, ?> options) Create a new transaction with the specified options.int
Get the index of the currently active database.Get most preferredFallbackTarget
.Get theFallbackTarget
(s).int
Get the configured target index to use when starting up for the very first time.Get the last time the standalone database was active.int
Get the maximum allowed target index.Get the configured "standalone mode"KVDatabase
to be used when allFallbackTarget
s are unavailable.Get this instance's persistent state file.int
Get the configured internal service thread priority.protected boolean
isMigrationAllowed
(int currTargetIndex, int nextTargetIndex) Subclass hook to veto an impending migration.protected void
migrationCompleted
(int prevTargetIndex, int currTargetIndex) Subclass hook to be notified when a migration occurs.void
setFallbackTarget
(FallbackTarget target) Configure a singleFallbackTarget
.void
setFallbackTargets
(List<? extends FallbackTarget> targets) Configure multipleFallbackTarget
(s).void
setInitialTargetIndex
(int initialTargetIndex) Configure the index of the currently active database when starting up for the very first time.void
setMaximumTargetIndex
(int maximumTargetIndex) Configure the maximum allowed target index.void
setStandaloneTarget
(KVDatabase standaloneKV) Configure the local "standalone mode"KVDatabase
to be used when allFallbackTarget
s are unavailable.void
setStateFile
(File stateFile) Configure this instance's persistent state file.void
setThreadPriority
(int threadPriority) Configure the priority of the internal service thread.void
start()
Start this instance.void
stop()
Stop this instance.toString()
-
Field Details
-
log
-
-
Constructor Details
-
FallbackKVDatabase
public FallbackKVDatabase()
-
-
Method Details
-
getStateFile
Get this instance's persistent state file.- Returns:
- file for persistent state
-
setStateFile
Configure this instance's persistent state file.Required property.
- Parameters:
stateFile
- file for persistent state- Throws:
IllegalArgumentException
- ifstateFile
is nullIllegalStateException
- if this instance is already started
-
getStandaloneTarget
Get the configured "standalone mode"KVDatabase
to be used when allFallbackTarget
s are unavailable.- Returns:
- "standalone mode" database
-
setStandaloneTarget
Configure the local "standalone mode"KVDatabase
to be used when allFallbackTarget
s are unavailable.- Parameters:
standaloneKV
- "standalone mode" database- Throws:
IllegalArgumentException
- ifstandaloneKV
is nullIllegalStateException
- if this instance is already started
-
getFallbackTarget
Get most preferredFallbackTarget
.Targets will be sorted in order of increasing preference.
- Returns:
- top fallback target, or null if none are configured yet
-
getFallbackTargets
Get theFallbackTarget
(s).Targets will be sorted in order of increasing preference.
- Returns:
- list of one or more fallback targets; the returned list is a snapshot-in-time copy of each target
-
setFallbackTarget
Configure a singleFallbackTarget
.- Parameters:
target
- fallback target- Throws:
IllegalArgumentException
- iftarget
is nullIllegalArgumentException
- if any target does not have aRaftKVDatabase
configuredIllegalStateException
- if this instance is already started
-
setFallbackTargets
Configure multipleFallbackTarget
(s).Targets should be sorted in order of increasing preference.
- Parameters:
targets
- targets in order of increasing preference- Throws:
IllegalArgumentException
- iftargets
is nullIllegalArgumentException
- iftargets
is emptyIllegalArgumentException
- if any target is nullIllegalArgumentException
- if any target does not have aRaftKVDatabase
configuredIllegalStateException
- if this instance is already started
-
getInitialTargetIndex
public int getInitialTargetIndex()Get the configured target index to use when starting up for the very first time.- Returns:
- initial target index, with -1 meaning standalone mode
-
setInitialTargetIndex
public void setInitialTargetIndex(int initialTargetIndex) Configure the index of the currently active database when starting up for the very first time. This value is only used on the initial startup; after that, the current fallback target is persisted across restarts.Default value is the most highly preferred target. Use -1 to change the default to standalone mode; this is appropriate when the Raft cluster is not yet configured on initial startup.
- Parameters:
initialTargetIndex
- initial target index, -1 meaning standalone mode; out of range values will be clipped
-
getCurrentTargetIndex
public int getCurrentTargetIndex()Get the index of the currently active database.- Returns:
- index into fallback target list, or -1 for standalone mode
-
setMaximumTargetIndex
public void setMaximumTargetIndex(int maximumTargetIndex) Configure the maximum allowed target index. This is a dynamic control that can be changed at runtime to force this instance into a lower index target (or standalone mode) than it would otherwise be in.Default value is
Integer.MAX_VALUE
.- Parameters:
maximumTargetIndex
- maximum target index, -1 meaning standalone mode; out of range values will be clipped
-
getMaximumTargetIndex
public int getMaximumTargetIndex()Get the maximum allowed target index.- Returns:
- maximum allowed target index; -1 for standalone mode
-
getLastStandaloneActiveTime
Get the last time the standalone database was active.- Returns:
- last active time of the standalone database, or null if never active
-
setThreadPriority
public void setThreadPriority(int threadPriority) Configure the priority of the internal service thread.Default is -1, which means do not change thread priority from its default.
- Parameters:
threadPriority
- internal service thread priority, or -1 to leave thread priority unchanged- Throws:
IllegalStateException
- if this instance is already startedIllegalArgumentException
- ifthreadPriority
is not -1 and not in the rangeThread.MIN_PRIORITY
toThread.MAX_PRIORITY
-
getThreadPriority
public int getThreadPriority()Get the configured internal service thread priority.- Returns:
- internal service thread priority, or -1 if not configured
-
start
@PostConstruct public void start()Description copied from interface:KVDatabase
Start this instance. This method must be called prior to creating any transactions.This method is idempotent: if this instance is already started, nothing happens.
Whether an instance that has been started and stopped can be restarted is implementation-dependent.
- Specified by:
start
in interfaceKVDatabase
-
stop
@PreDestroy public void stop()Description copied from interface:KVDatabase
Stop this instance.This method is idempotent: if this instance has not been started, or is already stopped, nothing happens.
- Specified by:
stop
in interfaceKVDatabase
-
createExecutorThread
Create the service thread used by this instance.The implementation in
FallbackKVDatabase
simply instantiates a thread and sets the name. Subclasses may override to set priority, etc.- Parameters:
action
- thread entry pointuniqueId
- unique ID for the thread which increments each time this instance is (re)started- Returns:
- service thread for this instance
-
createTransaction
Description copied from interface:KVDatabase
Create a new transaction.- Specified by:
createTransaction
in interfaceKVDatabase
- Returns:
- newly created transaction
-
createTransaction
Description copied from interface:KVDatabase
Create a new transaction with the specified options.- Specified by:
createTransaction
in interfaceKVDatabase
- Parameters:
options
- optional transaction options; may be null- Returns:
- newly created transaction
-
isMigrationAllowed
protected boolean isMigrationAllowed(int currTargetIndex, int nextTargetIndex) Subclass hook to veto an impending migration. This is invoked just prior to starting a migration. If this method returns false, the migration is deferred.The implementation in
FallbackKVDatabase
always returns true.- Parameters:
currTargetIndex
- current fallback target list index (before migration), or -1 for standalone modenextTargetIndex
- next fallback target list index (after migration), or -1 for standalone mode- Returns:
- true to allow migration to proceed, false to defer migration until later
-
migrationCompleted
protected void migrationCompleted(int prevTargetIndex, int currTargetIndex) Subclass hook to be notified when a migration occurs. This method is invoked after each successful target change.The implementation in
FallbackKVDatabase
does nothing.- Parameters:
prevTargetIndex
- previous fallback target list index, or -1 for standalone modecurrTargetIndex
- current fallback target list index, or -1 for standalone mode
-
toString
-
createSourceTransaction
Create a Raft source transaction.The implementation in
FallbackKVDatabase
returns a new read-only transaction with consistencyConsistency.EVENTUAL_COMMITTED
. The combination of read-only andConsistency.EVENTUAL_COMMITTED
is important, because this guarantees that the transaction will generate no network traffic (and not require any majority to exist) oncommit()
.- Parameters:
kvdb
- Raft database- Returns:
- new transaction for availability check
-
createDestinationTransaction
Create a Raft destination transaction.The implementation in
FallbackKVDatabase
just delegates toRaftKVDatabase.createTransaction()
.- Parameters:
kvdb
- Raft database- Returns:
- new transaction for availability check
-
createAvailabilityCheckTransaction
Create a Raft availability check transaction.The implementation in
FallbackKVDatabase
just delegates toRaftKVDatabase.createTransaction()
.- Parameters:
kvdb
- Raft database- Returns:
- new transaction for availability check
-