Optimistic locking option
This commit is contained in:
@@ -17,16 +17,29 @@ enum class ActionType {
|
||||
class TypeData(
|
||||
var nextId: AtomicLong = AtomicLong(1L),
|
||||
val data: MutableMap<Serializable, Persistable> = ConcurrentHashMap(),
|
||||
) : Serializable
|
||||
) : Serializable {
|
||||
companion object {
|
||||
private const val serialVersionUID: Long = 1L
|
||||
}
|
||||
}
|
||||
|
||||
class Action(
|
||||
val type: ActionType,
|
||||
val obj: Persistable
|
||||
) : Serializable
|
||||
) : Serializable {
|
||||
override fun toString(): String {
|
||||
return "Action(type=$type, obj=$obj)"
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val serialVersionUID: Long = 1L
|
||||
}
|
||||
}
|
||||
|
||||
class Datastore(
|
||||
private val directory: File,
|
||||
indexes: Array<PersistableIndex> = arrayOf(),
|
||||
val enableOptimisticLocking: Boolean = false,
|
||||
) {
|
||||
private val fileManager = FileManager(directory)
|
||||
private val transactionFormatter = DecimalFormat("#")
|
||||
@@ -58,7 +71,8 @@ class Datastore(
|
||||
|
||||
fun setMaxId(javaClass: Class<Persistable>, id: Long) {
|
||||
val nextId = data.getOrPut(javaClass) { TypeData() }.nextId
|
||||
if (nextId.get() <= id) nextId.set(id + 1)
|
||||
val current = nextId.get()
|
||||
if (current <= id) nextId.addAndGet(id - current)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
@@ -97,15 +111,24 @@ class Datastore(
|
||||
}
|
||||
}
|
||||
|
||||
Logger.debug("Loaded transactions in ${(System.nanoTime() - start) / 1_000_000}ms")
|
||||
Logger.debug("Loaded transactions in %6.3fms", ((System.nanoTime() - start) / 1_000_000f))
|
||||
}
|
||||
|
||||
private fun getTrnx(file: File): Long {
|
||||
return file.name.substringAfterLast('/').substringAfter("transaction-").substringBefore(".").toLong()
|
||||
}
|
||||
|
||||
fun execute(actions: MutableList<Action>) {
|
||||
private fun execute(actions: Set<Action>) {
|
||||
synchronized(this) {
|
||||
if (enableOptimisticLocking) {
|
||||
for (action in actions) {
|
||||
val typeData = data.getOrPut(action.obj::class.java) {
|
||||
TypeData()
|
||||
}
|
||||
if (action.type == ActionType.STORE) {
|
||||
if ((typeData.data[action.obj.id]?.version ?: -1L) >= action.obj.version) {
|
||||
throw OptimisticLockingException(action.obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (action in actions) {
|
||||
val typeData = data.getOrPut(action.obj::class.java) {
|
||||
TypeData()
|
||||
@@ -137,7 +160,6 @@ class Datastore(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun <T : Persistable> count(clazz: KClass<T>): Int {
|
||||
val typeData = data.getOrPut(clazz.java) {
|
||||
TypeData()
|
||||
@@ -176,25 +198,25 @@ class Datastore(
|
||||
return indexes[kClass.java]?.get(indexName)
|
||||
}
|
||||
|
||||
fun storeAndExecute(actions: MutableList<Action>) {
|
||||
fun storeAndExecute(actions: Set<Action>) {
|
||||
if (actions.isNotEmpty()) {
|
||||
synchronized(this) {
|
||||
writeTransaction(actions)
|
||||
execute(actions)
|
||||
writeTransaction(actions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun readTransaction(ois: ObjectInputStream) {
|
||||
val versionNumber = ois.readInt()
|
||||
check (versionNumber == 1) { "Unsupported version number: $versionNumber" }
|
||||
check(versionNumber == 1) { "Unsupported version number: $versionNumber" }
|
||||
val transactionNumber = ois.readLong()
|
||||
nextTransactionNumber = transactionNumber + 1
|
||||
val actions = ois.readObject() as MutableList<Action>
|
||||
val actions = ois.readObject() as Set<Action>
|
||||
execute(actions)
|
||||
}
|
||||
|
||||
private fun writeTransaction(actions: MutableList<Action>) {
|
||||
private fun writeTransaction(actions: Set<Action>) {
|
||||
val number = transactionFormatter.format(nextTransactionNumber)
|
||||
val file = File(directory, "transaction-$number.trn")
|
||||
ObjectOutputStream(file.outputStream()).use { oos ->
|
||||
@@ -226,12 +248,12 @@ class Datastore(
|
||||
}
|
||||
}
|
||||
}
|
||||
Logger.debug("Snapshot in ${(System.nanoTime() - start) / 1_000_000}ms")
|
||||
Logger.debug("Snapshot in %6.3fms", ((System.nanoTime() - start) / 1_000_000f))
|
||||
}
|
||||
|
||||
private fun readSnapshot(ois: ObjectInputStream) {
|
||||
val versionNumber = ois.readInt()
|
||||
check (versionNumber == 1) { "Unsupported version number: $versionNumber" }
|
||||
check(versionNumber == 1) { "Unsupported version number: $versionNumber" }
|
||||
nextTransactionNumber = ois.readLong() + 1
|
||||
data.clear()
|
||||
data.putAll(ois.readObject() as MutableMap<Class<*>, TypeData>)
|
||||
|
||||
Reference in New Issue
Block a user