Optimistic locking option

This commit is contained in:
2024-05-04 20:57:15 +02:00
parent cc3ac67be6
commit da046fa6ec
10 changed files with 281 additions and 26 deletions

View File

@@ -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>)