Skip to content

Commit

Permalink
Merge pull request #100 from JetBrains/adopt-orientdb-old-links
Browse files Browse the repository at this point in the history
Adopt orientdb old links
  • Loading branch information
leostryuk authored Sep 5, 2024
2 parents 76cbbf3 + e2c3040 commit 37760cc
Show file tree
Hide file tree
Showing 11 changed files with 199 additions and 98 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Copyright 2006 - 2024 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.exodus.database

import jetbrains.exodus.entitystore.Entity
import java.io.InputStream

interface TransientEntityOriginalValuesProvider {
fun getOriginalPropertyValue(e: TransientEntity, propertyName: String): Comparable<*>?
fun getOriginalBlobStringValue(e: TransientEntity, blobName: String): String?
fun getOriginalBlobValue(e: TransientEntity, blobName: String): InputStream?
fun getOriginalLinkValue(e: TransientEntity, linkName: String): Entity?
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,9 @@ interface TransientStoreSession : StoreTransaction {
*/
fun isRemoved(entity: Entity): Boolean

/**
* Flushes transaction without checking any constraints, without saving history and without versions check.
*/
fun quietIntermediateCommit()

fun setUpgradeHook(hook: Runnable?)

fun <T>getListenerTransientData(listener: DNQListener<*>): DnqListenerTransientData<T>

val originalValuesProvider: TransientEntityOriginalValuesProvider
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,6 @@ class ReadOnlyTransientSession(

override fun setUpgradeHook(hook: Runnable?) = throw UnsupportedOperationException()

override fun quietIntermediateCommit() = throw UnsupportedOperationException()

override fun flush() = throw UnsupportedOperationException()

override fun revert() = throw UnsupportedOperationException()
Expand Down Expand Up @@ -146,6 +144,8 @@ class ReadOnlyTransientSession(
override fun setRemoved(entity: Any) = Unit
}
}

override val originalValuesProvider = TransientEntityOriginalValuesProviderImpl(this)
}


Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,18 @@ import jetbrains.exodus.entitystore.Entity
import jetbrains.exodus.entitystore.EntityIterable
import jetbrains.exodus.entitystore.iterate.EntityIterableBase
import jetbrains.exodus.entitystore.orientdb.OEntity
import jetbrains.exodus.entitystore.orientdb.OEntityId
import jetbrains.exodus.entitystore.orientdb.OVertexEntity
import jetbrains.exodus.entitystore.orientdb.asReadonly
import java.io.File
import java.io.InputStream

class ReadonlyTransientEntityImpl(change: TransientEntityChange?, snapshot: OEntity, store: TransientEntityStore) :
TransientEntityImpl(snapshot, store) {
class ReadonlyTransientEntityImpl(change: TransientEntityChange?, snapshot: OEntity, store: TransientEntityStore) : TransientEntityImpl(snapshot, store) {

constructor(snapshot: OEntity, store: TransientEntityStore) : this(null, snapshot, store)

private val originalValuesProvider get() = threadSessionOrThrow.originalValuesProvider

private val hasChanges by lazy {
changedProperties.isNotEmpty() || changedLinks.values.any { it.isNotEmpty() }
}
Expand All @@ -43,6 +45,12 @@ class ReadonlyTransientEntityImpl(change: TransientEntityChange?, snapshot: OEnt
override val isReadonly: Boolean
get() = true

//region readonly throw

override fun delete(): Boolean {
throwReadonlyException()
}

override fun setProperty(propertyName: String, value: Comparable<*>): Boolean {
throwReadonlyException()
}
Expand Down Expand Up @@ -82,28 +90,42 @@ class ReadonlyTransientEntityImpl(change: TransientEntityChange?, snapshot: OEnt
override fun deleteLinks(linkName: String) {
throwReadonlyException()
}
//endregion

override fun getLink(linkName: String): Entity? {
return (entity.getLink(linkName) as OVertexEntity?)
?.let { linkTarget ->
ReadonlyTransientEntityImpl(linkTarget.asReadonly(), store)
}
return originalValuesProvider.getOriginalLinkValue(this, linkName)
}

override fun getLink(linkName: String, session: TransientStoreSession?): Entity? {
return getLink(linkName)
}

override fun getLinks(linkName: String): EntityIterable {
return PersistentEntityIterableWrapper(store, entity.getLinks(linkName))
//this will definitely fail in case of concurrent modification
// we get the current state and revert changes that have happened during the transaction
val oldLinksState = entity
.getLinks(linkName)
.map { ReadonlyTransientEntityImpl(null, it as OEntity ,store) }
.toSet()
.plus(getRemovedLinks(linkName))
.minus(getAddedLinks(linkName))
return (oldLinksState as Set<TransientEntity>).asEntityIterable()
}

override fun getLinks(linkNames: Collection<String>): EntityIterable {
throw UnsupportedOperationException()
override fun getProperty(propertyName: String): Comparable<*>? {
return originalValuesProvider.getOriginalPropertyValue(this, propertyName)
}

override fun delete(): Boolean {
throwReadonlyException()
override fun getBlobString(blobName: String): String? {
return originalValuesProvider.getOriginalBlobStringValue(this, blobName)
}

override fun getBlob(blobName: String): InputStream? {
return originalValuesProvider.getOriginalBlobValue(this, blobName)
}

override fun getLinks(linkNames: Collection<String>): EntityIterable {
throw UnsupportedOperationException()
}

override fun hasChanges() = hasChanges
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ open class RemovedTransientEntity(
private val transientEntity: TransientEntity
) : TransientEntity {

val originalValuesProvider get() = transientEntity.threadSessionOrThrow.originalValuesProvider

private val _id = transientEntity.id

override val isNew: Boolean
Expand Down Expand Up @@ -244,7 +246,7 @@ open class RemovedTransientEntity(
}

override fun getLink(linkName: String): Entity? {
val linkValue = (threadSessionOrThrow as TransientSessionImpl).getOriginalLinkValue(this, linkName)
val linkValue = originalValuesProvider.getOriginalLinkValue(this, linkName)
return linkValue
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import jetbrains.exodus.core.dataStructures.hash.LinkedHashSet
import jetbrains.exodus.database.*
import jetbrains.exodus.entitystore.Entity
import jetbrains.exodus.entitystore.iterate.EntityIdSet
import jetbrains.exodus.entitystore.orientdb.OEntity
import jetbrains.exodus.entitystore.util.EntityIdSetFactory
import java.math.BigInteger
import java.util.*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class TransientEntitiesUpdaterImpl(
private val changes = QueueDecorator<() -> Boolean>()

private val transientChangesTracker get() = session.transientChangesTracker
private val originalValuesProvider get() = session.originalValuesProvider

private var allowRunnable = true

Expand Down Expand Up @@ -65,7 +66,7 @@ class TransientEntitiesUpdaterImpl(
propertyNewValue: Comparable<*>
): Boolean {
return if (transientEntity.entity.setProperty(propertyName, propertyNewValue)) {
val oldValue = session.getOriginalPropertyValue(transientEntity, propertyName)
val oldValue = originalValuesProvider.getOriginalPropertyValue(transientEntity, propertyName)
@Suppress("SuspiciousEqualsCombination")
if (propertyNewValue === oldValue || propertyNewValue == oldValue) {
transientChangesTracker.removePropertyChanged(transientEntity, propertyName)
Expand All @@ -84,7 +85,7 @@ class TransientEntitiesUpdaterImpl(

private fun deletePropertyInternal(transientEntity: TransientEntity, propertyName: String): Boolean {
return if (transientEntity.entity.deleteProperty(propertyName)) {
val oldValue = session.getOriginalPropertyValue(transientEntity, propertyName)
val oldValue = originalValuesProvider.getOriginalPropertyValue(transientEntity, propertyName)
if (oldValue == null) {
transientChangesTracker.removePropertyChanged(transientEntity, propertyName)
} else {
Expand All @@ -99,7 +100,7 @@ class TransientEntitiesUpdaterImpl(
override fun setBlobString(transientEntity: TransientEntity, blobName: String, newValue: String): Boolean {
return addChangeAndRun {
transientEntity.entity.setBlobString(blobName, newValue)
val oldValue = session.getOriginalBlobStringValue(transientEntity, blobName)
val oldValue = originalValuesProvider.getOriginalBlobStringValue(transientEntity, blobName)
if (newValue === oldValue || newValue == oldValue) {
transientChangesTracker.removePropertyChanged(transientEntity, blobName)
} else {
Expand All @@ -112,7 +113,7 @@ class TransientEntitiesUpdaterImpl(
override fun deleteBlob(transientEntity: TransientEntity, blobName: String): Boolean {
return addChangeAndRun {
if (transientEntity.entity.deleteBlob(blobName)) {
val oldValue = session.getOriginalBlobValue(transientEntity, blobName)
val oldValue = originalValuesProvider.getOriginalBlobValue(transientEntity, blobName)
if (oldValue == null) {
transientChangesTracker.removePropertyChanged(transientEntity, blobName)
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/**
* Copyright 2006 - 2024 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.teamsys.dnq.database

import com.orientechnologies.orient.core.db.ODatabaseSession
import com.orientechnologies.orient.core.record.OVertex
import com.orientechnologies.orient.core.record.impl.ORecordBytes
import jetbrains.exodus.database.LinkChangeType
import jetbrains.exodus.database.TransientEntity
import jetbrains.exodus.database.TransientEntityOriginalValuesProvider
import jetbrains.exodus.database.TransientStoreSession
import jetbrains.exodus.entitystore.Entity
import jetbrains.exodus.entitystore.orientdb.OComparableSet
import jetbrains.exodus.util.UTFUtil
import java.io.ByteArrayInputStream
import java.io.InputStream

class TransientEntityOriginalValuesProviderImpl(private val session: TransientStoreSession) : TransientEntityOriginalValuesProvider {
private val transientChangesTracker get() = session.transientChangesTracker

override fun getOriginalPropertyValue(e: TransientEntity, propertyName: String): Comparable<*>? {
val session = ODatabaseSession.getActiveSession()
val id = e.entity.id.asOId()
val oVertex = session.load<OVertex>(id)
val onLoadValue = oVertex.getPropertyOnLoadValue<Any>(propertyName)
return if (onLoadValue is MutableSet<*>) {
OComparableSet(onLoadValue)
} else {
onLoadValue as Comparable<*>?
}
}

override fun getOriginalBlobStringValue(e: TransientEntity, blobName: String): String? {
val session = ODatabaseSession.getActiveSession()
val id = e.entity.id.asOId()
val oVertex = session.load<OVertex>(id)
val blobHolder = oVertex.getPropertyOnLoadValue<ORecordBytes?>(blobName)
return blobHolder?.toStream()?.let {
UTFUtil.readUTF((it).inputStream())
}
}

override fun getOriginalBlobValue(e: TransientEntity, blobName: String): InputStream? {
val session = ODatabaseSession.getActiveSession()
val id = e.entity.id.asOId()
val oVertex = session.load<OVertex>(id)
val blobHolder = oVertex.getPropertyOnLoadValue<ORecordBytes?>(blobName)
return blobHolder?.let {
ByteArrayInputStream(blobHolder.toStream())
}
}

override fun getOriginalLinkValue(e: TransientEntity, linkName: String): Entity? {
// get from saved changes, if not - from db
val change = transientChangesTracker.getChangedLinksDetailed(e)?.get(linkName)
if (change != null) {
when (change.changeType) {
LinkChangeType.ADD_AND_REMOVE,
LinkChangeType.REMOVE -> {
return if (change.removedEntitiesSize != 1) {
if (change.deletedEntitiesSize == 1) {
change.deletedEntities!!.iterator().next()
} else {
throw IllegalStateException("Can't determine original link value: ${e.type}.$linkName")
}
} else {
change.removedEntities!!.iterator().next()
}
}

else ->
throw IllegalStateException("Incorrect change type for link that is part of index: ${e.type}.$linkName: ${change.changeType.getName()}")
}
}
return e.entity.getLink(linkName)
}

}
Loading

0 comments on commit 37760cc

Please sign in to comment.