mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 09:17:43 +03:00
Add string-interning data access [DPP-707] (#11484)
* Add new table, and party_id to party_entries * Add write side basics (DbDto, Schema) * Add read side basic (StorageBackendStringInterning) * Add StorageBackend unit test * Support for all DB backends changelog_begin changelog_end
This commit is contained in:
parent
6c9416492d
commit
541d07c3a9
@ -1 +1 @@
|
||||
86ded6839752d76bb3e3e6cde7f4ec103dea6cee6382b6695de95e84dd74d2da
|
||||
e28b88accecead16dcd67becabf7ad230654b65f93c277e785ba77203c3db8b5
|
||||
|
@ -539,3 +539,8 @@ SELECT
|
||||
exercise_result_compression
|
||||
FROM participant_events_non_consuming_exercise
|
||||
;
|
||||
|
||||
CREATE TABLE string_interning (
|
||||
internal_id integer PRIMARY KEY NOT NULL,
|
||||
external_string text
|
||||
);
|
@ -0,0 +1 @@
|
||||
f87eb3e6709109d9fa34a9757cf53e3f8b84584a079442eea6e8da4f4224dc2e
|
@ -0,0 +1,7 @@
|
||||
-- Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
-- SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
CREATE TABLE string_interning (
|
||||
internal_id NUMBER PRIMARY KEY NOT NULL,
|
||||
external_string VARCHAR2(4000)
|
||||
);
|
@ -0,0 +1 @@
|
||||
d807d5d30cc3c3a55ad6a7319f21cc8a77f78c0a76fbdc94cd3c6a895e432fb7
|
@ -0,0 +1,7 @@
|
||||
-- Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
-- SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
CREATE TABLE string_interning (
|
||||
internal_id INTEGER PRIMARY KEY NOT NULL,
|
||||
external_string TEXT
|
||||
);
|
@ -135,4 +135,8 @@ object DbDto {
|
||||
|
||||
final case class CommandDeduplication(deduplication_key: String) extends DbDto
|
||||
|
||||
final case class StringInterningDto(
|
||||
internalId: Int,
|
||||
externalString: String,
|
||||
) extends DbDto
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ trait StorageBackend[DB_BATCH]
|
||||
with DBLockStorageBackend
|
||||
with IntegrityStorageBackend
|
||||
with ResetStorageBackend
|
||||
with StringInterningStorageBackend
|
||||
|
||||
trait ResetStorageBackend {
|
||||
|
||||
@ -352,6 +353,12 @@ trait IntegrityStorageBackend {
|
||||
def verifyIntegrity()(connection: Connection): Unit
|
||||
}
|
||||
|
||||
trait StringInterningStorageBackend {
|
||||
def loadStringInterningEntries(fromIdExclusive: Int, untilIdInclusive: Int)(
|
||||
connection: Connection
|
||||
): Iterable[(Int, String)]
|
||||
}
|
||||
|
||||
object StorageBackend {
|
||||
case class RawContractState(
|
||||
templateId: Option[String],
|
||||
|
@ -22,6 +22,7 @@ trait StorageBackendFactory {
|
||||
def createDBLockStorageBackend: DBLockStorageBackend
|
||||
def createIntegrityStorageBackend: IntegrityStorageBackend
|
||||
def createResetStorageBackend: ResetStorageBackend
|
||||
def createStringInterningStorageBackend: StringInterningStorageBackend
|
||||
}
|
||||
|
||||
object StorageBackendFactory {
|
||||
|
@ -65,9 +65,11 @@ private[backend] case class Bytea[FROM](extract: FROM => Array[Byte])
|
||||
private[backend] case class ByteaOptional[FROM](extract: FROM => Option[Array[Byte]])
|
||||
extends TrivialOptionalField[FROM, Array[Byte]]
|
||||
|
||||
private[backend] case class Integer[FROM](extract: FROM => Int) extends TrivialField[FROM, Int]
|
||||
|
||||
private[backend] case class IntOptional[FROM](extract: FROM => Option[Int])
|
||||
extends Field[FROM, Option[Int], java.lang.Integer] {
|
||||
override def convert: Option[Int] => Integer = _.map(Int.box).orNull
|
||||
override def convert: Option[Int] => java.lang.Integer = _.map(Int.box).orNull
|
||||
}
|
||||
|
||||
private[backend] case class Bigint[FROM](extract: FROM => Long) extends TrivialField[FROM, Long]
|
||||
@ -79,7 +81,7 @@ private[backend] case class BigintOptional[FROM](extract: FROM => Option[Long])
|
||||
|
||||
private[backend] case class SmallintOptional[FROM](extract: FROM => Option[Int])
|
||||
extends Field[FROM, Option[Int], java.lang.Integer] {
|
||||
override def convert: Option[Int] => Integer = _.map(Int.box).orNull
|
||||
override def convert: Option[Int] => java.lang.Integer = _.map(Int.box).orNull
|
||||
}
|
||||
|
||||
private[backend] case class BooleanField[FROM](extract: FROM => Boolean)
|
||||
|
@ -52,6 +52,9 @@ private[backend] object AppendOnlySchema {
|
||||
def smallintOptional[FROM, _](extractor: FROM => Option[Int]): Field[FROM, Option[Int], _] =
|
||||
SmallintOptional(extractor)
|
||||
|
||||
def int[FROM, _](extractor: FROM => Int): Field[FROM, Int, _] =
|
||||
Integer(extractor)
|
||||
|
||||
def intOptional[FROM, _](extractor: FROM => Option[Int]): Field[FROM, Option[Int], _] =
|
||||
IntOptional(extractor)
|
||||
|
||||
@ -227,6 +230,12 @@ private[backend] object AppendOnlySchema {
|
||||
"deduplication_key" -> fieldStrategy.string(_.deduplication_key)
|
||||
)
|
||||
|
||||
val stringInterningTable: Table[DbDto.StringInterningDto] =
|
||||
fieldStrategy.insert("string_interning")(
|
||||
"internal_id" -> fieldStrategy.int(_.internalId),
|
||||
"external_string" -> fieldStrategy.string(_.externalString),
|
||||
)
|
||||
|
||||
val executes: Seq[Array[Array[_]] => Connection => Unit] = List(
|
||||
eventsDivulgence.executeUpdate,
|
||||
eventsCreate.executeUpdate,
|
||||
@ -238,6 +247,7 @@ private[backend] object AppendOnlySchema {
|
||||
partyEntries.executeUpdate,
|
||||
commandCompletions.executeUpdate,
|
||||
commandSubmissionDeletes.executeUpdate,
|
||||
stringInterningTable.executeUpdate,
|
||||
)
|
||||
|
||||
new Schema[DbDto] {
|
||||
@ -257,6 +267,7 @@ private[backend] object AppendOnlySchema {
|
||||
partyEntries.prepareData(collect[PartyEntry]),
|
||||
commandCompletions.prepareData(collect[CommandCompletion]),
|
||||
commandSubmissionDeletes.prepareData(collect[CommandDeduplication]),
|
||||
stringInterningTable.prepareData(collect[StringInterningDto]),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.platform.store.backend.common
|
||||
|
||||
import java.sql.Connection
|
||||
|
||||
import anorm.SqlParser.{int, str}
|
||||
import anorm.{RowParser, SqlStringInterpolation, ~}
|
||||
import com.daml.platform.store.backend.StringInterningStorageBackend
|
||||
import com.daml.platform.store.SimpleSqlAsVectorOf.SimpleSqlAsVectorOf
|
||||
|
||||
object StringInterningStorageBackendTemplate extends StringInterningStorageBackend {
|
||||
|
||||
private val StringInterningEntriesParser: RowParser[(Int, String)] =
|
||||
int("internal_id") ~ str("external_string") map { case internalId ~ externalString =>
|
||||
(internalId, externalString)
|
||||
}
|
||||
|
||||
override def loadStringInterningEntries(fromIdExclusive: Int, untilIdInclusive: Int)(
|
||||
connection: Connection
|
||||
): Iterable[(Int, String)] =
|
||||
SQL"""
|
||||
SELECT internal_id, external_string
|
||||
FROM string_interning
|
||||
WHERE
|
||||
internal_id > $fromIdExclusive
|
||||
AND internal_id <= $untilIdInclusive
|
||||
""".asVectorOf(StringInterningEntriesParser)(connection)
|
||||
}
|
@ -22,6 +22,7 @@ object H2ResetStorageBackend extends ResetStorageBackend {
|
||||
|truncate table participant_events_consuming_exercise;
|
||||
|truncate table participant_events_non_consuming_exercise;
|
||||
|truncate table party_entries;
|
||||
|truncate table string_interning;
|
||||
|set referential_integrity true;""".stripMargin)
|
||||
.execute()(connection)
|
||||
()
|
||||
@ -40,6 +41,7 @@ object H2ResetStorageBackend extends ResetStorageBackend {
|
||||
|truncate table participant_events_consuming_exercise;
|
||||
|truncate table participant_events_non_consuming_exercise;
|
||||
|truncate table party_entries;
|
||||
|truncate table string_interning;
|
||||
|set referential_integrity true;""".stripMargin)
|
||||
.execute()(connection)
|
||||
()
|
||||
|
@ -11,6 +11,7 @@ import com.daml.platform.store.backend.common.{
|
||||
PackageStorageBackendTemplate,
|
||||
ParameterStorageBackendTemplate,
|
||||
PartyStorageBackendTemplate,
|
||||
StringInterningStorageBackendTemplate,
|
||||
}
|
||||
import com.daml.platform.store.backend.{
|
||||
CompletionStorageBackend,
|
||||
@ -27,6 +28,7 @@ import com.daml.platform.store.backend.{
|
||||
PartyStorageBackend,
|
||||
ResetStorageBackend,
|
||||
StorageBackendFactory,
|
||||
StringInterningStorageBackend,
|
||||
}
|
||||
|
||||
object H2StorageBackendFactory extends StorageBackendFactory {
|
||||
@ -68,4 +70,7 @@ object H2StorageBackendFactory extends StorageBackendFactory {
|
||||
|
||||
override val createResetStorageBackend: ResetStorageBackend =
|
||||
H2ResetStorageBackend
|
||||
|
||||
override val createStringInterningStorageBackend: StringInterningStorageBackend =
|
||||
StringInterningStorageBackendTemplate
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ object OracleResetStorageBackend extends ResetStorageBackend {
|
||||
"truncate table participant_events_consuming_exercise cascade",
|
||||
"truncate table participant_events_non_consuming_exercise cascade",
|
||||
"truncate table party_entries cascade",
|
||||
"truncate table string_interning cascade",
|
||||
).map(SQL(_)).foreach(_.execute()(connection))
|
||||
|
||||
override def resetAll(connection: Connection): Unit =
|
||||
@ -36,5 +37,6 @@ object OracleResetStorageBackend extends ResetStorageBackend {
|
||||
"truncate table participant_events_consuming_exercise cascade",
|
||||
"truncate table participant_events_non_consuming_exercise cascade",
|
||||
"truncate table party_entries cascade",
|
||||
"truncate table string_interning cascade",
|
||||
).map(SQL(_)).foreach(_.execute()(connection))
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import com.daml.platform.store.backend.common.{
|
||||
PackageStorageBackendTemplate,
|
||||
ParameterStorageBackendTemplate,
|
||||
PartyStorageBackendTemplate,
|
||||
StringInterningStorageBackendTemplate,
|
||||
}
|
||||
import com.daml.platform.store.backend.{
|
||||
CompletionStorageBackend,
|
||||
@ -28,6 +29,7 @@ import com.daml.platform.store.backend.{
|
||||
PartyStorageBackend,
|
||||
ResetStorageBackend,
|
||||
StorageBackendFactory,
|
||||
StringInterningStorageBackend,
|
||||
}
|
||||
|
||||
object OracleStorageBackendFactory extends StorageBackendFactory {
|
||||
@ -69,4 +71,7 @@ object OracleStorageBackendFactory extends StorageBackendFactory {
|
||||
|
||||
override val createResetStorageBackend: ResetStorageBackend =
|
||||
OracleResetStorageBackend
|
||||
|
||||
override val createStringInterningStorageBackend: StringInterningStorageBackend =
|
||||
StringInterningStorageBackendTemplate
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ object PostgresResetStorageBackend extends ResetStorageBackend {
|
||||
|truncate table participant_events_consuming_exercise cascade;
|
||||
|truncate table participant_events_non_consuming_exercise cascade;
|
||||
|truncate table party_entries cascade;
|
||||
|truncate table string_interning cascade;
|
||||
|""".stripMargin)
|
||||
.execute()(connection)
|
||||
()
|
||||
@ -37,6 +38,7 @@ object PostgresResetStorageBackend extends ResetStorageBackend {
|
||||
|truncate table participant_events_consuming_exercise cascade;
|
||||
|truncate table participant_events_non_consuming_exercise cascade;
|
||||
|truncate table party_entries cascade;
|
||||
|truncate table string_interning cascade;
|
||||
|""".stripMargin)
|
||||
.execute()(connection)
|
||||
()
|
||||
|
@ -12,6 +12,7 @@ import com.daml.platform.store.backend.common.{
|
||||
PackageStorageBackendTemplate,
|
||||
ParameterStorageBackendTemplate,
|
||||
PartyStorageBackendTemplate,
|
||||
StringInterningStorageBackendTemplate,
|
||||
}
|
||||
import com.daml.platform.store.backend.{
|
||||
CompletionStorageBackend,
|
||||
@ -28,6 +29,7 @@ import com.daml.platform.store.backend.{
|
||||
PartyStorageBackend,
|
||||
ResetStorageBackend,
|
||||
StorageBackendFactory,
|
||||
StringInterningStorageBackend,
|
||||
}
|
||||
|
||||
object PostgresStorageBackendFactory extends StorageBackendFactory {
|
||||
@ -69,4 +71,7 @@ object PostgresStorageBackendFactory extends StorageBackendFactory {
|
||||
|
||||
override val createResetStorageBackend: ResetStorageBackend =
|
||||
PostgresResetStorageBackend
|
||||
|
||||
override val createStringInterningStorageBackend: StringInterningStorageBackend =
|
||||
StringInterningStorageBackendTemplate
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ trait StorageBackendSuite
|
||||
with StorageBackendTestsDBLockForSuite
|
||||
with StorageBackendTestsIntegrity
|
||||
with StorageBackendTestsDeduplication
|
||||
with StorageBackendTestsTimestamps {
|
||||
with StorageBackendTestsTimestamps
|
||||
with StorageBackendTestsStringInterning {
|
||||
this: AsyncFlatSpec =>
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ private[backend] trait StorageBackendTestsReset extends Matchers with StorageBac
|
||||
backendFactory.createPackageStorageBackend
|
||||
private val contractStorageBackend: ContractStorageBackend =
|
||||
backendFactory.createContractStorageBackend
|
||||
private val stringInterningStorageBackend: StringInterningStorageBackend =
|
||||
backendFactory.createStringInterningStorageBackend
|
||||
private val resetStorageBackend: ResetStorageBackend = backendFactory.createResetStorageBackend
|
||||
|
||||
behavior of "StorageBackend (reset)"
|
||||
@ -34,6 +36,9 @@ private[backend] trait StorageBackendTestsReset extends Matchers with StorageBac
|
||||
config <- executeSql(configurationStorageBackend.ledgerConfiguration)
|
||||
packages <- executeSql(packageStorageBackend.lfPackages)
|
||||
events <- executeSql(contractStorageBackend.contractStateEvents(0, Long.MaxValue))
|
||||
stringInterningEntries <- executeSql(
|
||||
stringInterningStorageBackend.loadStringInterningEntries(0, 1000)
|
||||
)
|
||||
} yield {
|
||||
identity shouldBe None
|
||||
end shouldBe None
|
||||
@ -41,6 +46,7 @@ private[backend] trait StorageBackendTestsReset extends Matchers with StorageBac
|
||||
packages shouldBe empty
|
||||
events shouldBe empty
|
||||
config shouldBe None
|
||||
stringInterningEntries shouldBe empty
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,6 +79,7 @@ private[backend] trait StorageBackendTestsReset extends Matchers with StorageBac
|
||||
dtoExercise(offset(5), 2L, true, "#4"),
|
||||
dtoDivulgence(Some(offset(5)), 3L, "#4"),
|
||||
dtoCompletion(offset(5)),
|
||||
DbDto.StringInterningDto(2, "2"),
|
||||
)
|
||||
|
||||
for {
|
||||
@ -94,6 +101,9 @@ private[backend] trait StorageBackendTestsReset extends Matchers with StorageBac
|
||||
parties <- executeSql(partyStorageBackend.knownParties)
|
||||
config <- executeSql(configurationStorageBackend.ledgerConfiguration)
|
||||
packages <- executeSql(packageStorageBackend.lfPackages)
|
||||
stringInterningEntries <- executeSql(
|
||||
stringInterningStorageBackend.loadStringInterningEntries(0, 1000)
|
||||
)
|
||||
} yield {
|
||||
identity shouldBe None
|
||||
end shouldBe None
|
||||
@ -101,6 +111,7 @@ private[backend] trait StorageBackendTestsReset extends Matchers with StorageBac
|
||||
packages should not be empty // Note: reset() does not delete packages
|
||||
events shouldBe empty
|
||||
config shouldBe None
|
||||
stringInterningEntries shouldBe empty
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,6 +152,9 @@ private[backend] trait StorageBackendTestsReset extends Matchers with StorageBac
|
||||
parties <- executeSql(partyStorageBackend.knownParties)
|
||||
config <- executeSql(configurationStorageBackend.ledgerConfiguration)
|
||||
packages <- executeSql(packageStorageBackend.lfPackages)
|
||||
stringInterningEntries <- executeSql(
|
||||
stringInterningStorageBackend.loadStringInterningEntries(0, 1000)
|
||||
)
|
||||
} yield {
|
||||
identity shouldBe None
|
||||
end shouldBe None
|
||||
@ -148,6 +162,7 @@ private[backend] trait StorageBackendTestsReset extends Matchers with StorageBac
|
||||
packages shouldBe empty // Note: resetAll() does delete packages
|
||||
events shouldBe empty
|
||||
config shouldBe None
|
||||
stringInterningEntries shouldBe empty
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.platform.store.backend
|
||||
|
||||
import org.scalatest.Inside
|
||||
import org.scalatest.flatspec.AsyncFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
private[backend] trait StorageBackendTestsStringInterning
|
||||
extends Matchers
|
||||
with Inside
|
||||
with StorageBackendSpec {
|
||||
this: AsyncFlatSpec =>
|
||||
|
||||
private val stringInterningStorageBackend = backendFactory.createStringInterningStorageBackend
|
||||
|
||||
behavior of "StorageBackend (StringInterning)"
|
||||
|
||||
it should "store and load string-interning entries" in {
|
||||
val dtos = Vector(
|
||||
DbDto.StringInterningDto(2, "a"),
|
||||
DbDto.StringInterningDto(3, "b"),
|
||||
DbDto.StringInterningDto(4, "c"),
|
||||
DbDto.StringInterningDto(5, "d"),
|
||||
)
|
||||
|
||||
for {
|
||||
interningIdsBeforeBegin <- executeSql(
|
||||
stringInterningStorageBackend.loadStringInterningEntries(0, 5)
|
||||
)
|
||||
_ <- executeSql(ingest(dtos, _))
|
||||
interningIdsFull <- executeSql(stringInterningStorageBackend.loadStringInterningEntries(0, 5))
|
||||
interningIdsOverFetch <- executeSql(
|
||||
stringInterningStorageBackend.loadStringInterningEntries(0, 10)
|
||||
)
|
||||
interningIdsEmpty <- executeSql(
|
||||
stringInterningStorageBackend.loadStringInterningEntries(5, 10)
|
||||
)
|
||||
interningIdsSubset <- executeSql(
|
||||
stringInterningStorageBackend.loadStringInterningEntries(3, 10)
|
||||
)
|
||||
} yield {
|
||||
val expectedFullList = List(
|
||||
2 -> "a",
|
||||
3 -> "b",
|
||||
4 -> "c",
|
||||
5 -> "d",
|
||||
)
|
||||
interningIdsBeforeBegin shouldBe Nil
|
||||
interningIdsFull shouldBe expectedFullList
|
||||
interningIdsOverFetch shouldBe expectedFullList
|
||||
interningIdsEmpty shouldBe Nil
|
||||
interningIdsSubset shouldBe expectedFullList.drop(2)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user