mirror of
https://github.com/corda/corda.git
synced 2024-12-29 09:18:58 +00:00
[ENT-1639] Add version
column (#585)
* Add `version` column The equality check of the timestamp was tricky to get right. Better to use a logical timestamp.
This commit is contained in:
parent
24366012e2
commit
5def901980
@ -29,6 +29,7 @@ const val ID = "mutual_exclusion_id"
|
|||||||
const val MACHINE_NAME = "machine_name"
|
const val MACHINE_NAME = "machine_name"
|
||||||
const val PID = "pid"
|
const val PID = "pid"
|
||||||
const val TIMESTAMP = "mutual_exclusion_timestamp"
|
const val TIMESTAMP = "mutual_exclusion_timestamp"
|
||||||
|
const val VERSION = "version"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes sure only one node is able to write to database.
|
* Makes sure only one node is able to write to database.
|
||||||
@ -56,7 +57,7 @@ class RunOnceService(private val database: CordaPersistence, private val machine
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = TABLE)
|
@Table(name = TABLE)
|
||||||
class MutualExclusion(machineNameInit: String, pidInit: String, timeStampInit: LocalDateTime) {
|
class MutualExclusion(machineNameInit: String, pidInit: String, timeStampInit: LocalDateTime, versionInit: Long = 0) {
|
||||||
@Column(name = ID, insertable = false, updatable = false)
|
@Column(name = ID, insertable = false, updatable = false)
|
||||||
@Id
|
@Id
|
||||||
val id: Char = 'X'
|
val id: Char = 'X'
|
||||||
@ -69,6 +70,9 @@ class RunOnceService(private val database: CordaPersistence, private val machine
|
|||||||
|
|
||||||
@Column(name = TIMESTAMP)
|
@Column(name = TIMESTAMP)
|
||||||
val timestamp = timeStampInit
|
val timestamp = timeStampInit
|
||||||
|
|
||||||
|
@Column(name = VERSION)
|
||||||
|
val version = versionInit
|
||||||
}
|
}
|
||||||
|
|
||||||
fun start() {
|
fun start() {
|
||||||
@ -119,10 +123,11 @@ class RunOnceService(private val database: CordaPersistence, private val machine
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun insertMutualExclusion(session: Session) {
|
private fun insertMutualExclusion(session: Session) {
|
||||||
val query = session.createNativeQuery("INSERT INTO $TABLE VALUES ('X', :machineName, :pid, CURRENT_TIMESTAMP)", MutualExclusion::class.java)
|
val query = session.createNativeQuery("INSERT INTO $TABLE VALUES ('X', :machineName, :pid, CURRENT_TIMESTAMP, :version)", MutualExclusion::class.java)
|
||||||
query.unwrap(org.hibernate.SQLQuery::class.java).addSynchronizedEntityClass(MutualExclusion::class.java)
|
query.unwrap(org.hibernate.SQLQuery::class.java).addSynchronizedEntityClass(MutualExclusion::class.java)
|
||||||
query.setParameter("pid", pid)
|
query.setParameter("pid", pid)
|
||||||
query.setParameter("machineName", machineName)
|
query.setParameter("machineName", machineName)
|
||||||
|
query.setParameter("version", 0)
|
||||||
val returnValue = query.executeUpdate()
|
val returnValue = query.executeUpdate()
|
||||||
|
|
||||||
if (returnValue != 1) {
|
if (returnValue != 1) {
|
||||||
@ -138,23 +143,27 @@ class RunOnceService(private val database: CordaPersistence, private val machine
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateTimestamp(session: Session, mutualExclusion: MutualExclusion): Boolean {
|
private fun updateTimestamp(session: Session, mutualExclusion: MutualExclusion): Boolean {
|
||||||
val minWaitTime = mutualExclusion.timestamp.plus(waitInterval, ChronoField.MILLI_OF_SECOND.baseUnit)
|
|
||||||
val hql = "UPDATE RunOnceService\$MutualExclusion SET $MACHINE_NAME = :machineName, $TIMESTAMP = CURRENT_TIMESTAMP, $PID = :pid " +
|
val hql = "UPDATE RunOnceService\$MutualExclusion SET $MACHINE_NAME = :machineName, $TIMESTAMP = CURRENT_TIMESTAMP, $PID = :pid, $VERSION = :newVersion " +
|
||||||
"WHERE $ID = 'X' AND " +
|
"WHERE $ID = 'X' AND " +
|
||||||
// we are master node
|
// we are master node
|
||||||
"($MACHINE_NAME = :machineName OR " +
|
"($MACHINE_NAME = :machineName OR " +
|
||||||
// change master node
|
// change master node
|
||||||
"($MACHINE_NAME != :machineName AND " +
|
"($MACHINE_NAME != :machineName AND " +
|
||||||
// no one else has updated timestamp whilst we attempted this update
|
// no one else has updated timestamp whilst we attempted this update
|
||||||
"$TIMESTAMP = CAST(:mutualExclusionTimestamp AS LocalDateTime) AND " +
|
"$VERSION = :oldVersion AND " +
|
||||||
// old timestamp
|
// old timestamp
|
||||||
"CURRENT_TIMESTAMP > CAST(:waitTime AS LocalDateTime)))"
|
"CAST(CURRENT_TIMESTAMP as LocalDateTime) > CAST(:waitTime as LocalDateTime)))"
|
||||||
|
|
||||||
val query = session.createQuery(hql)
|
val query = session.createQuery(hql)
|
||||||
|
|
||||||
|
val oldVersion = mutualExclusion.version
|
||||||
|
val minWaitTime = mutualExclusion.timestamp.plus(waitInterval, ChronoField.MILLI_OF_SECOND.baseUnit)
|
||||||
|
|
||||||
query.setParameter("pid", pid)
|
query.setParameter("pid", pid)
|
||||||
query.setParameter("machineName", machineName)
|
query.setParameter("machineName", machineName)
|
||||||
query.setParameter("mutualExclusionTimestamp", mutualExclusion.timestamp)
|
query.setParameter("oldVersion", oldVersion)
|
||||||
|
query.setParameter("newVersion", oldVersion+1)
|
||||||
query.setParameter("waitTime", minWaitTime)
|
query.setParameter("waitTime", minWaitTime)
|
||||||
val returnValue = query.executeUpdate()
|
val returnValue = query.executeUpdate()
|
||||||
|
|
||||||
|
@ -20,4 +20,13 @@
|
|||||||
<!--this is needed because pre-v3 attachments can't be used-->
|
<!--this is needed because pre-v3 attachments can't be used-->
|
||||||
<delete tableName="node_attachments"/>
|
<delete tableName="node_attachments"/>
|
||||||
</changeSet>
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="R3.Corda" id="add_version_column_to_mutual_exclusion_table">
|
||||||
|
<addColumn tableName="node_mutual_exclusion">
|
||||||
|
<column name="version" type="BIGINT">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
@ -146,11 +146,14 @@ class RunOnceServiceTest {
|
|||||||
|
|
||||||
var secondTimestamp = LocalDateTime.now()
|
var secondTimestamp = LocalDateTime.now()
|
||||||
var firstTimestamp = LocalDateTime.now()
|
var firstTimestamp = LocalDateTime.now()
|
||||||
|
var firstVersion = -1L
|
||||||
|
var secondVersion = -1L
|
||||||
|
|
||||||
database.transaction {
|
database.transaction {
|
||||||
val query = session.createNativeQuery(selectQuery, RunOnceService.MutualExclusion::class.java)
|
val query = session.createNativeQuery(selectQuery, RunOnceService.MutualExclusion::class.java)
|
||||||
val result = machine1RowCheck(query)
|
val result = machine1RowCheck(query)
|
||||||
firstTimestamp = result.timestamp
|
firstTimestamp = result.timestamp
|
||||||
|
firstVersion = result.version
|
||||||
}
|
}
|
||||||
|
|
||||||
runnable.run()
|
runnable.run()
|
||||||
@ -159,9 +162,11 @@ class RunOnceServiceTest {
|
|||||||
val query = session.createNativeQuery(selectQuery, RunOnceService.MutualExclusion::class.java)
|
val query = session.createNativeQuery(selectQuery, RunOnceService.MutualExclusion::class.java)
|
||||||
val result = machine1RowCheck(query)
|
val result = machine1RowCheck(query)
|
||||||
secondTimestamp = result.timestamp
|
secondTimestamp = result.timestamp
|
||||||
|
secondVersion = result.version
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(secondTimestamp.isAfter(firstTimestamp))
|
assertTrue(secondTimestamp.isAfter(firstTimestamp))
|
||||||
|
assertTrue(secondVersion > firstVersion)
|
||||||
|
|
||||||
mock<ScheduledFuture<*>>()
|
mock<ScheduledFuture<*>>()
|
||||||
}
|
}
|
||||||
@ -171,7 +176,6 @@ class RunOnceServiceTest {
|
|||||||
verify(mockUpdateExecutor).scheduleAtFixedRate(any(), any(), any(), any())
|
verify(mockUpdateExecutor).scheduleAtFixedRate(any(), any(), any(), any())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `timer exits if no row`() {
|
fun `timer exits if no row`() {
|
||||||
exit.expectSystemExitWithStatus(1)
|
exit.expectSystemExitWithStatus(1)
|
||||||
@ -255,3 +259,4 @@ class RunOnceServiceTest {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user