Skip to content

Commit

Permalink
Merge pull request #122 from JetBrains/adopt-orientdb-rewrite-old-value
Browse files Browse the repository at this point in the history
Adopt orientdb rewrite old value
  • Loading branch information
leostryuk authored Dec 12, 2024
2 parents 99a24ec + 6bad8d0 commit 63e986e
Show file tree
Hide file tree
Showing 17 changed files with 258 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,14 @@ interface TransientChangesTracker {

fun linksRemoved(source: TransientEntity, linkName: String, links: @JvmSuppressWildcards Iterable<Entity>)

fun propertyChanged(e: TransientEntity, propertyName: String)
/**
* Called when a property value of an entity changes.
*
* @param e The entity for which the property was changed.
* @param propertyName The name of the property being changed.
* @param oldValue current value of entity property. It may be different for the same propertyName within the same transaction
*/
fun propertyChanged(e: TransientEntity, propertyName: String, oldValue: Comparable<*>?)

fun removePropertyChanged(e: TransientEntity, propertyName: String)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ interface TransientEntitiesUpdater {

fun deleteProperty(transientEntity: TransientEntity, propertyName: String): Boolean

fun setBlobString(transientEntity: TransientEntity, blobName: String, newValue: String): Boolean
fun setBlobString(transientEntity: TransientEntity, blobName: String, newValue: String?): Boolean
fun deleteBlob(transientEntity: TransientEntity, blobName: String): Boolean
fun setLink(source: TransientEntity, linkName: String, target: TransientEntity): Boolean
fun addLink(source: TransientEntity, linkName: String, target: TransientEntity): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,6 @@ interface TransientEntity : Entity {
fun clearChildren(parentToChildLinkName: String)

fun addChild(parentToChildLinkName: String, childToParentLinkName: String, child: Entity)

fun deleteStringBlob(blobName: String): Boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ package jetbrains.exodus.database

interface TransientEntityChange {
val transientEntity: TransientEntity
val changedProperties: Set<String>?
val changedProperties: Map<String, Comparable<*>?>?
val changedLinksDetailed: Map<String, LinkChange>?
val changeType: EntityChangeType
val snapshotEntity: TransientEntity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class ReadOnlyTransientChangesTrackerImpl : TransientChangesTracker {
override fun linksRemoved(source: TransientEntity, linkName: String, links: Iterable<Entity>): Unit =
throw UnsupportedOperationException()

override fun propertyChanged(e: TransientEntity, propertyName: String): Unit =
override fun propertyChanged(e: TransientEntity, propertyName: String, oldValue: Comparable<*>?) =
throw UnsupportedOperationException()

override fun removePropertyChanged(e: TransientEntity, propertyName: String): Unit =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class ReadonlyTransientEntitiesUpdater : TransientEntitiesUpdater {
throw IllegalStateException("Readonly transaction cannot perform write operations")
}

override fun setBlobString(transientEntity: TransientEntity, blobName: String, newValue: String): Boolean {
override fun setBlobString(transientEntity: TransientEntity, blobName: String, newValue: String?): Boolean {
throw IllegalStateException("Readonly transaction cannot perform write operations")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ class ReadonlyTransientEntityImpl(change: TransientEntityChange?, snapshot: OEnt
throwReadonlyException()
}

override fun deleteStringBlob(blobName: String): Boolean {
throwReadonlyException()
}

override fun deleteLinks(linkName: String) {
throwReadonlyException()
}
Expand All @@ -97,6 +101,7 @@ class ReadonlyTransientEntityImpl(change: TransientEntityChange?, snapshot: OEnt
return getLink(linkName)
}

@Suppress("UNCHECKED_CAST")
override fun getLinks(linkName: String): EntityIterable {
//this will definitely fail in case of concurrent modification
// we get the current state and revert changes that have happened during the transaction
Expand All @@ -111,11 +116,23 @@ class ReadonlyTransientEntityImpl(change: TransientEntityChange?, snapshot: OEnt
}

override fun getProperty(propertyName: String): Comparable<*>? {
return originalValuesProvider.getOriginalPropertyValue(this, propertyName)
return if (changedProperties.containsKey(propertyName)){
changedProperties[propertyName]
} else {
entity.getProperty(propertyName)
}
}

override fun getBlobString(blobName: String): String? {
return originalValuesProvider.getOriginalBlobStringValue(this, blobName)
return if (changedProperties.containsKey(blobName)) {
changedProperties[blobName] as String?
} else {
entity.getBlobString(blobName)
}
}

override fun getPropertyOldValue(propertyName: String): Comparable<*>? {
return getProperty(propertyName)
}

override fun getBlob(blobName: String): InputStream? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ open class RemovedTransientEntity(
throw IllegalStateException("Entity is removed")
}

override fun deleteStringBlob(blobName: String): Boolean {
throw IllegalStateException("Entity is removed")
}

override fun addLink(linkName: String, target: Entity): Boolean {
throw IllegalStateException("Entity is removed")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
*/
package com.jetbrains.teamsys.dnq.database

import com.orientechnologies.orient.core.db.ODatabaseSession
import com.orientechnologies.orient.core.record.OVertex
import jetbrains.exodus.core.dataStructures.decorators.HashMapDecorator
import jetbrains.exodus.core.dataStructures.decorators.LinkedHashSetDecorator
import jetbrains.exodus.core.dataStructures.hash.HashMap
Expand All @@ -32,7 +30,7 @@ import java.util.*
/**
* @author Vadim.Gurov
*/
class TransientChangesTrackerImpl() : TransientChangesTracker {
class TransientChangesTrackerImpl : TransientChangesTracker {

private val _changedEntities = LinkedHashSet<TransientEntity>()
override val changedEntities: Set<TransientEntity>
Expand All @@ -48,20 +46,22 @@ class TransientChangesTrackerImpl() : TransientChangesTracker {

private val removedFrom = HashMapDecorator<TransientEntity, MutableList<LinkChange>>()
private val entityToChangedLinksDetailed = HashMapDecorator<TransientEntity, MutableMap<String, LinkChange>>()
private val entityToChangedProperties = HashMapDecorator<TransientEntity, MutableSet<String>>()
private val entityToChangedPropertiesOldValues =
HashMapDecorator<TransientEntity, HashMap<String, Comparable<*>?>>()

// do not take into consideration RemovedNew entities - such entities was created and removed during same transaction
override val changesHash: BigInteger
get() = changedEntities
.filterNot { it.id in addedEntities && it.id in removedEntities }
.sortedBy { it.id }
.filter { e -> entityToChangedProperties[e].isNotEmpty || entityToChangedLinksDetailed[e].isNotEmpty }
.fold(BigInteger.ONE) { hc, entity ->
var h = hc.applyHashCode(entity.id).applyHashCode(getEntityChangeType(entity))
getChangedProperties(entity)?.sorted()?.forEach { propertyName ->
h = h.applyHashCode(propertyName)
}
getChangedLinksDetailed(entity)?.values?.filter { it.isNotEmpty() }?.sortedBy { it.linkName }?.forEach { linkChange ->
.filterNot { it.id in addedEntities && it.id in removedEntities }
.sortedBy { it.id }
.filter { e -> entityToChangedPropertiesOldValues[e]?.isNotEmpty() == true || entityToChangedLinksDetailed[e].isNotEmpty }
.fold(BigInteger.ONE) { hc, entity ->
var h = hc.applyHashCode(entity.id).applyHashCode(getEntityChangeType(entity))
getChangedProperties(entity)?.sorted()?.forEach { propertyName ->
h = h.applyHashCode(propertyName)
}
getChangedLinksDetailed(entity)?.values?.filter { it.isNotEmpty() }?.sortedBy { it.linkName }
?.forEach { linkChange ->
h = h.applyHashCode(linkChange.changeType)
h = h.applyHashCode(linkChange.addedEntitiesSize)
h = h.applyHashCode(linkChange.removedEntitiesSize)
Expand All @@ -76,16 +76,22 @@ class TransientChangesTrackerImpl() : TransientChangesTracker {
h = h.applyHashCode(id)
}
}
h
}
h
}

// do not notify about RemovedNew entities - such entities were created and removed during same transaction
override val changesDescription: Set<TransientEntityChange>
get() = changedEntities
.filterNot { it.id in addedEntities && it.id in removedEntities }
.mapTo(LinkedHashSetDecorator()) {
TransientEntityChangeImpl(this, it, getChangedProperties(it), getChangedLinksDetailed(it), getEntityChangeType(it))
}
.filterNot { it.id in addedEntities && it.id in removedEntities }
.mapTo(LinkedHashSetDecorator()) {
TransientEntityChangeImpl(
this,
it,
entityToChangedPropertiesOldValues[it],
getChangedLinksDetailed(it),
getEntityChangeType(it)
)
}

override val changesDescriptionCount: Int
get() {
Expand All @@ -94,7 +100,11 @@ class TransientChangesTrackerImpl() : TransientChangesTracker {
}

override fun getSnapshotEntity(transientEntity: TransientEntity): TransientEntityImpl {
return ReadonlyTransientEntityImpl(getChangeDescription(transientEntity), transientEntity.entity, transientEntity.store)
return ReadonlyTransientEntityImpl(
getChangeDescription(transientEntity),
transientEntity.entity,
transientEntity.store
)
}

private fun getEntityChangeType(transientEntity: TransientEntity): EntityChangeType {
Expand All @@ -107,11 +117,11 @@ class TransientChangesTrackerImpl() : TransientChangesTracker {

override fun getChangeDescription(transientEntity: TransientEntity): TransientEntityChange {
return TransientEntityChangeImpl(
this,
transientEntity,
getChangedProperties(transientEntity),
getChangedLinksDetailed(transientEntity),
getEntityChangeType(transientEntity)
this,
transientEntity,
entityToChangedPropertiesOldValues[transientEntity],
getChangedLinksDetailed(transientEntity),
getEntityChangeType(transientEntity)
)
}

Expand All @@ -120,25 +130,27 @@ class TransientChangesTrackerImpl() : TransientChangesTracker {
}

override fun getChangedProperties(transientEntity: TransientEntity): Set<String>? {
return entityToChangedProperties[transientEntity]
return entityToChangedPropertiesOldValues[transientEntity]?.keys
}

override fun hasChanges(transientEntity: TransientEntity): Boolean =
!getChangedProperties(transientEntity).isNullOrEmpty() || !getChangedLinksDetailed(transientEntity).isNullOrEmpty()
!getChangedProperties(transientEntity).isNullOrEmpty() || !getChangedLinksDetailed(transientEntity).isNullOrEmpty()

override fun hasPropertyChanges(transientEntity: TransientEntity, propName: String): Boolean =
getChangedProperties(transientEntity).orEmpty().contains(propName)
getChangedProperties(transientEntity).orEmpty().contains(propName)

override fun hasLinkChanges(transientEntity: TransientEntity, linkName: String): Boolean =
getChangedLinksDetailed(transientEntity).orEmpty().containsKey(linkName)
getChangedLinksDetailed(transientEntity).orEmpty().containsKey(linkName)

override fun getPropertyOldValue(transientEntity: TransientEntity, propName: String): Comparable<*>? {
val session = ODatabaseSession.getActiveSession()
val id = transientEntity.entity.id.asOId()
val oVertex = session.load<OVertex>(id)
return oVertex.getPropertyOnLoadValue(propName)
}
val entityOldValues = entityToChangedPropertiesOldValues[transientEntity]
return if (entityOldValues?.contains(propName) == true){
return entityOldValues.get(propName)
} else {
transientEntity.getProperty(propName)
}

}

override fun isNew(transientEntity: TransientEntity): Boolean {
return transientEntity.id in addedEntities
Expand All @@ -165,13 +177,22 @@ class TransientChangesTrackerImpl() : TransientChangesTracker {
}
}

private fun getLinkChange(source: TransientEntity, linkName: String): Pair<MutableMap<String, LinkChange>, LinkChange> {
private fun getLinkChange(
source: TransientEntity,
linkName: String
): Pair<MutableMap<String, LinkChange>, LinkChange> {
val linksDetailed = entityToChangedLinksDetailed.getOrPut(source) { HashMap() }
val linkChange = linksDetailed.getOrPut(linkName) { LinkChange(linkName) }
return Pair(linksDetailed, linkChange)
}

override fun linkChanged(source: TransientEntity, linkName: String, target: TransientEntity, oldTarget: TransientEntity?, add: Boolean) {
override fun linkChanged(
source: TransientEntity,
linkName: String,
target: TransientEntity,
oldTarget: TransientEntity?,
add: Boolean
) {
entityChanged(source)

val (linksDetailed, linkChange) = getLinkChange(source, linkName)
Expand Down Expand Up @@ -202,19 +223,21 @@ class TransientChangesTrackerImpl() : TransientChangesTracker {
_affectedEntityTypes.add(e.type)
}

override fun propertyChanged(e: TransientEntity, propertyName: String) {
override fun propertyChanged(e: TransientEntity, propertyName: String, oldValue: Comparable<*>?) {
entityChanged(e)

val properties = entityToChangedProperties.getOrPut(e) { HashSet() }
properties.add(propertyName)
val oldValues = entityToChangedPropertiesOldValues.getOrPut(e) { HashMap() }
if (!oldValues.contains(propertyName)) {
oldValues[propertyName] = oldValue
}
}

override fun removePropertyChanged(e: TransientEntity, propertyName: String) {
val properties = entityToChangedProperties[e]
val properties = entityToChangedPropertiesOldValues[e]
if (properties != null) {
properties.remove(propertyName)
if (properties.isEmpty()) {
entityToChangedProperties.remove(e)
entityToChangedPropertiesOldValues.remove(e)
}
}
}
Expand Down
Loading

0 comments on commit 63e986e

Please sign in to comment.