mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 01:07:18 +03:00
Sandbox on H2 - performance improvements for the append-only schema [DPP-600] (#10888)
* Added --database-connection-pool-size parameter to the sandbox CLI * Improved maximum ledger time lookup query * Improved active contract lookup query * Improved max event sequential id query CHANGELOG_BEGIN - [Sandbox] - Added a CLI parameter for configuring the number of connections in the database connection pool used for serving ledger API requests CHANGELOG_END
This commit is contained in:
parent
9a1a1015f2
commit
a939594025
@ -58,7 +58,7 @@ private[appendonlydao] final class TransactionsReader(
|
|||||||
|
|
||||||
private val dbMetrics = metrics.daml.index.db
|
private val dbMetrics = metrics.daml.index.db
|
||||||
private val eventSeqIdReader =
|
private val eventSeqIdReader =
|
||||||
new EventsRange.EventSeqIdReader(storageBackend.maxEventSeqIdForOffset)
|
new EventsRange.EventSeqIdReader(storageBackend.maxEventSequentialIdOfAnObservableEvent)
|
||||||
private val getTransactions =
|
private val getTransactions =
|
||||||
new EventsTableFlatEventsRangeQueries.GetTransactions(storageBackend)
|
new EventsTableFlatEventsRangeQueries.GetTransactions(storageBackend)
|
||||||
private val getActiveContracts =
|
private val getActiveContracts =
|
||||||
|
@ -268,7 +268,9 @@ trait EventStorageBackend {
|
|||||||
transactionId: Ref.TransactionId,
|
transactionId: Ref.TransactionId,
|
||||||
filterParams: FilterParams,
|
filterParams: FilterParams,
|
||||||
)(connection: Connection): Vector[EventsTable.Entry[Raw.TreeEvent]]
|
)(connection: Connection): Vector[EventsTable.Entry[Raw.TreeEvent]]
|
||||||
def maxEventSeqIdForOffset(offset: Offset)(connection: Connection): Option[Long]
|
|
||||||
|
/** Max event sequential id of observable (create, consuming and nonconsuming exercise) events. */
|
||||||
|
def maxEventSequentialIdOfAnObservableEvent(offset: Offset)(connection: Connection): Option[Long]
|
||||||
def rawEvents(startExclusive: Long, endInclusive: Long)(
|
def rawEvents(startExclusive: Long, endInclusive: Long)(
|
||||||
connection: Connection
|
connection: Connection
|
||||||
): Vector[RawTransactionEvent]
|
): Vector[RawTransactionEvent]
|
||||||
|
@ -5,9 +5,8 @@ package com.daml.platform.store.backend.common
|
|||||||
|
|
||||||
import java.sql.Connection
|
import java.sql.Connection
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
import anorm.SqlParser.{binaryStream, int, long, str}
|
import anorm.SqlParser.{binaryStream, int, long, str}
|
||||||
import anorm.{ResultSetParser, RowParser, SqlParser, ~}
|
import anorm.{ResultSetParser, Row, RowParser, SimpleSql, SqlParser, ~}
|
||||||
import com.daml.lf.data.Ref
|
import com.daml.lf.data.Ref
|
||||||
import com.daml.platform.store.Conversions.{
|
import com.daml.platform.store.Conversions.{
|
||||||
contractId,
|
contractId,
|
||||||
@ -52,17 +51,9 @@ trait ContractStorageBackendTemplate extends ContractStorageBackend {
|
|||||||
s"The following contracts have not been found: ${missingContractIds.map(_.coid).mkString(", ")}"
|
s"The following contracts have not been found: ${missingContractIds.map(_.coid).mkString(", ")}"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO append-only: revisit this approach when doing cleanup, so we can decide if it is enough or not.
|
protected def maximumLedgerTimeSqlLiteral(id: ContractId): SimpleSql[Row] = {
|
||||||
// TODO append-only: consider pulling up traversal logic to upper layer
|
import com.daml.platform.store.Conversions.ContractIdToStatement
|
||||||
override def maximumLedgerTime(
|
SQL"""
|
||||||
ids: Set[ContractId]
|
|
||||||
)(connection: Connection): Try[Option[Instant]] = {
|
|
||||||
if (ids.isEmpty) {
|
|
||||||
Failure(emptyContractIds)
|
|
||||||
} else {
|
|
||||||
def lookup(id: ContractId): Option[Option[Instant]] = {
|
|
||||||
import com.daml.platform.store.Conversions.ContractIdToStatement
|
|
||||||
SQL"""
|
|
||||||
WITH archival_event AS (
|
WITH archival_event AS (
|
||||||
SELECT participant_events.*
|
SELECT participant_events.*
|
||||||
FROM participant_events, parameters
|
FROM participant_events, parameters
|
||||||
@ -99,8 +90,20 @@ trait ContractStorageBackendTemplate extends ContractStorageBackend {
|
|||||||
FROM create_and_divulged_contracts
|
FROM create_and_divulged_contracts
|
||||||
WHERE NOT EXISTS (SELECT 1 FROM archival_event)
|
WHERE NOT EXISTS (SELECT 1 FROM archival_event)
|
||||||
FETCH NEXT 1 ROW ONLY"""
|
FETCH NEXT 1 ROW ONLY"""
|
||||||
.as(instantFromMicros("ledger_effective_time").?.singleOpt)(connection)
|
}
|
||||||
}
|
|
||||||
|
// TODO append-only: revisit this approach when doing cleanup, so we can decide if it is enough or not.
|
||||||
|
// TODO append-only: consider pulling up traversal logic to upper layer
|
||||||
|
override def maximumLedgerTime(
|
||||||
|
ids: Set[ContractId]
|
||||||
|
)(connection: Connection): Try[Option[Instant]] = {
|
||||||
|
if (ids.isEmpty) {
|
||||||
|
Failure(emptyContractIds)
|
||||||
|
} else {
|
||||||
|
def lookup(id: ContractId): Option[Option[Instant]] =
|
||||||
|
maximumLedgerTimeSqlLiteral(id).as(instantFromMicros("ledger_effective_time").?.singleOpt)(
|
||||||
|
connection
|
||||||
|
)
|
||||||
|
|
||||||
val queriedIds: List[(ContractId, Option[Option[Instant]])] = ids.toList
|
val queriedIds: List[(ContractId, Option[Option[Instant]])] = ids.toList
|
||||||
.map(id => id -> lookup(id))
|
.map(id => id -> lookup(id))
|
||||||
@ -223,24 +226,13 @@ trait ContractStorageBackendTemplate extends ContractStorageBackend {
|
|||||||
.map(SqlParser.flatten)
|
.map(SqlParser.flatten)
|
||||||
.map(StorageBackend.RawContract.tupled)
|
.map(StorageBackend.RawContract.tupled)
|
||||||
|
|
||||||
private def activeContract[T](
|
protected def activeContractSqlLiteral(
|
||||||
resultSetParser: ResultSetParser[T],
|
|
||||||
resultColumns: List[String],
|
|
||||||
)(
|
|
||||||
readers: Set[Ref.Party],
|
|
||||||
contractId: ContractId,
|
contractId: ContractId,
|
||||||
)(connection: Connection): T = {
|
treeEventWitnessesClause: CompositeSql,
|
||||||
|
resultColumns: List[String],
|
||||||
|
coalescedColumns: String,
|
||||||
|
): SimpleSql[Row] = {
|
||||||
import com.daml.platform.store.Conversions.ContractIdToStatement
|
import com.daml.platform.store.Conversions.ContractIdToStatement
|
||||||
val treeEventWitnessesClause =
|
|
||||||
queryStrategy.arrayIntersectionNonEmptyClause(
|
|
||||||
columnName = "tree_event_witnesses",
|
|
||||||
parties = readers,
|
|
||||||
)
|
|
||||||
val coalescedColumns = resultColumns
|
|
||||||
.map(columnName =>
|
|
||||||
s"COALESCE(divulgence_events.$columnName, create_event_unrestricted.$columnName)"
|
|
||||||
)
|
|
||||||
.mkString(", ")
|
|
||||||
SQL""" WITH archival_event AS (
|
SQL""" WITH archival_event AS (
|
||||||
SELECT participant_events.*
|
SELECT participant_events.*
|
||||||
FROM participant_events, parameters
|
FROM participant_events, parameters
|
||||||
@ -296,6 +288,26 @@ trait ContractStorageBackendTemplate extends ContractStorageBackend {
|
|||||||
FROM create_and_divulged_contracts
|
FROM create_and_divulged_contracts
|
||||||
WHERE NOT EXISTS (SELECT 1 FROM archival_event)
|
WHERE NOT EXISTS (SELECT 1 FROM archival_event)
|
||||||
FETCH NEXT 1 ROW ONLY"""
|
FETCH NEXT 1 ROW ONLY"""
|
||||||
|
}
|
||||||
|
|
||||||
|
private def activeContract[T](
|
||||||
|
resultSetParser: ResultSetParser[T],
|
||||||
|
resultColumns: List[String],
|
||||||
|
)(
|
||||||
|
readers: Set[Ref.Party],
|
||||||
|
contractId: ContractId,
|
||||||
|
)(connection: Connection): T = {
|
||||||
|
val treeEventWitnessesClause: CompositeSql =
|
||||||
|
queryStrategy.arrayIntersectionNonEmptyClause(
|
||||||
|
columnName = "tree_event_witnesses",
|
||||||
|
parties = readers,
|
||||||
|
)
|
||||||
|
val coalescedColumns: String = resultColumns
|
||||||
|
.map(columnName =>
|
||||||
|
s"COALESCE(divulgence_events.$columnName, create_event_unrestricted.$columnName)"
|
||||||
|
)
|
||||||
|
.mkString(", ")
|
||||||
|
activeContractSqlLiteral(contractId, treeEventWitnessesClause, resultColumns, coalescedColumns)
|
||||||
.as(resultSetParser)(connection)
|
.as(resultSetParser)(connection)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,12 +5,12 @@ package com.daml.platform.store.backend.h2
|
|||||||
|
|
||||||
import java.sql.Connection
|
import java.sql.Connection
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
import anorm.{Row, SQL, SimpleSql}
|
||||||
import anorm.SQL
|
|
||||||
import anorm.SqlParser.get
|
import anorm.SqlParser.get
|
||||||
import com.daml.ledger.offset.Offset
|
import com.daml.ledger.offset.Offset
|
||||||
import com.daml.lf.data.Ref
|
import com.daml.lf.data.Ref
|
||||||
import com.daml.logging.LoggingContext
|
import com.daml.logging.LoggingContext
|
||||||
|
import com.daml.platform.store.appendonlydao.events.ContractId
|
||||||
import com.daml.platform.store.backend.EventStorageBackend.FilterParams
|
import com.daml.platform.store.backend.EventStorageBackend.FilterParams
|
||||||
import com.daml.platform.store.backend.common.ComposableQuery.{CompositeSql, SqlStringInterpolation}
|
import com.daml.platform.store.backend.common.ComposableQuery.{CompositeSql, SqlStringInterpolation}
|
||||||
import com.daml.platform.store.backend.common.{
|
import com.daml.platform.store.backend.common.{
|
||||||
@ -32,6 +32,7 @@ import com.daml.platform.store.backend.{
|
|||||||
StorageBackend,
|
StorageBackend,
|
||||||
common,
|
common,
|
||||||
}
|
}
|
||||||
|
|
||||||
import javax.sql.DataSource
|
import javax.sql.DataSource
|
||||||
|
|
||||||
private[backend] object H2StorageBackend
|
private[backend] object H2StorageBackend
|
||||||
@ -105,14 +106,21 @@ private[backend] object H2StorageBackend
|
|||||||
override def insertBatch(connection: Connection, batch: AppendOnlySchema.Batch): Unit =
|
override def insertBatch(connection: Connection, batch: AppendOnlySchema.Batch): Unit =
|
||||||
H2Schema.schema.executeUpdate(batch, connection)
|
H2Schema.schema.executeUpdate(batch, connection)
|
||||||
|
|
||||||
// TODO FIXME: this is for postgres not for H2
|
def maxEventSequentialIdOfAnObservableEvent(
|
||||||
def maxEventSeqIdForOffset(offset: Offset)(connection: Connection): Option[Long] = {
|
offset: Offset
|
||||||
|
)(connection: Connection): Option[Long] = {
|
||||||
import com.daml.platform.store.Conversions.OffsetToStatement
|
import com.daml.platform.store.Conversions.OffsetToStatement
|
||||||
// This query could be: "select max(event_sequential_id) from participant_events where event_offset <= ${range.endInclusive}"
|
SQL"""
|
||||||
// however tests using PostgreSQL 12 with tens of millions of events have shown that the index
|
SELECT max_esi FROM (
|
||||||
// on `event_offset` is not used unless we _hint_ at it by specifying `order by event_offset`
|
(SELECT max(event_sequential_id) AS max_esi FROM participant_events_consuming_exercise WHERE event_offset <= $offset GROUP BY event_offset ORDER BY event_offset DESC FETCH NEXT 1 ROW ONLY)
|
||||||
SQL"select max(event_sequential_id) from participant_events where event_offset <= $offset group by event_offset order by event_offset desc limit 1"
|
UNION ALL
|
||||||
.as(get[Long](1).singleOpt)(connection)
|
(SELECT max(event_sequential_id) AS max_esi FROM participant_events_non_consuming_exercise WHERE event_offset <= $offset GROUP BY event_offset ORDER BY event_offset DESC FETCH NEXT 1 ROW ONLY)
|
||||||
|
UNION ALL
|
||||||
|
(SELECT max(event_sequential_id) AS max_esi FROM participant_events_create WHERE event_offset <= $offset GROUP BY event_offset ORDER BY event_offset DESC FETCH NEXT 1 ROW ONLY)
|
||||||
|
) AS t
|
||||||
|
ORDER BY max_esi DESC
|
||||||
|
FETCH NEXT 1 ROW ONLY;
|
||||||
|
""".as(get[Long](1).singleOpt)(connection)
|
||||||
}
|
}
|
||||||
|
|
||||||
object H2QueryStrategy extends QueryStrategy {
|
object H2QueryStrategy extends QueryStrategy {
|
||||||
@ -238,4 +246,103 @@ private[backend] object H2StorageBackend
|
|||||||
pruneAllDivulgedContracts: Boolean,
|
pruneAllDivulgedContracts: Boolean,
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
): Unit = ()
|
): Unit = ()
|
||||||
|
|
||||||
|
override def maximumLedgerTimeSqlLiteral(id: ContractId): SimpleSql[Row] = {
|
||||||
|
import com.daml.platform.store.Conversions.ContractIdToStatement
|
||||||
|
SQL"""
|
||||||
|
WITH archival_event AS (
|
||||||
|
SELECT 1
|
||||||
|
FROM participant_events_consuming_exercise, parameters
|
||||||
|
WHERE contract_id = $id
|
||||||
|
AND event_sequential_id <= parameters.ledger_end_sequential_id
|
||||||
|
FETCH NEXT 1 ROW ONLY
|
||||||
|
),
|
||||||
|
create_event AS (
|
||||||
|
SELECT ledger_effective_time
|
||||||
|
FROM participant_events_create, parameters
|
||||||
|
WHERE contract_id = $id
|
||||||
|
AND event_sequential_id <= parameters.ledger_end_sequential_id
|
||||||
|
FETCH NEXT 1 ROW ONLY -- limit here to guide planner wrt expected number of results
|
||||||
|
),
|
||||||
|
divulged_contract AS (
|
||||||
|
SELECT NULL::BIGINT
|
||||||
|
FROM participant_events_divulgence, parameters
|
||||||
|
WHERE contract_id = $id
|
||||||
|
AND event_sequential_id <= parameters.ledger_end_sequential_id
|
||||||
|
ORDER BY event_sequential_id
|
||||||
|
-- prudent engineering: make results more stable by preferring earlier divulgence events
|
||||||
|
-- Results might still change due to pruning.
|
||||||
|
FETCH NEXT 1 ROW ONLY
|
||||||
|
),
|
||||||
|
create_and_divulged_contracts AS (
|
||||||
|
(SELECT * FROM create_event) -- prefer create over divulgence events
|
||||||
|
UNION ALL
|
||||||
|
(SELECT * FROM divulged_contract)
|
||||||
|
)
|
||||||
|
SELECT ledger_effective_time
|
||||||
|
FROM create_and_divulged_contracts
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM archival_event)
|
||||||
|
FETCH NEXT 1 ROW ONLY"""
|
||||||
|
}
|
||||||
|
|
||||||
|
override def activeContractSqlLiteral(
|
||||||
|
contractId: ContractId,
|
||||||
|
treeEventWitnessesClause: CompositeSql,
|
||||||
|
resultColumns: List[String],
|
||||||
|
coalescedColumns: String,
|
||||||
|
): SimpleSql[Row] = {
|
||||||
|
import com.daml.platform.store.Conversions.ContractIdToStatement
|
||||||
|
SQL""" WITH archival_event AS (
|
||||||
|
SELECT 1
|
||||||
|
FROM participant_events_consuming_exercise, parameters
|
||||||
|
WHERE contract_id = $contractId
|
||||||
|
AND event_sequential_id <= parameters.ledger_end_sequential_id
|
||||||
|
AND $treeEventWitnessesClause -- only use visible archivals
|
||||||
|
FETCH NEXT 1 ROW ONLY
|
||||||
|
),
|
||||||
|
create_event AS (
|
||||||
|
SELECT contract_id, #${resultColumns.mkString(", ")}
|
||||||
|
FROM participant_events_create, parameters
|
||||||
|
WHERE contract_id = $contractId
|
||||||
|
AND event_sequential_id <= parameters.ledger_end_sequential_id
|
||||||
|
AND $treeEventWitnessesClause
|
||||||
|
FETCH NEXT 1 ROW ONLY -- limit here to guide planner wrt expected number of results
|
||||||
|
),
|
||||||
|
-- no visibility check, as it is used to backfill missing template_id and create_arguments for divulged contracts
|
||||||
|
create_event_unrestricted AS (
|
||||||
|
SELECT contract_id, #${resultColumns.mkString(", ")}
|
||||||
|
FROM participant_events_create, parameters
|
||||||
|
WHERE contract_id = $contractId
|
||||||
|
AND event_sequential_id <= parameters.ledger_end_sequential_id
|
||||||
|
FETCH NEXT 1 ROW ONLY -- limit here to guide planner wrt expected number of results
|
||||||
|
),
|
||||||
|
divulged_contract AS (
|
||||||
|
SELECT divulgence_events.contract_id,
|
||||||
|
-- Note: the divulgence_event.template_id can be NULL
|
||||||
|
-- for certain integrations. For example, the KV integration exploits that
|
||||||
|
-- every participant node knows about all create events. The integration
|
||||||
|
-- therefore only communicates the change in visibility to the IndexDB, but
|
||||||
|
-- does not include a full divulgence event.
|
||||||
|
#$coalescedColumns
|
||||||
|
FROM participant_events_divulgence divulgence_events LEFT OUTER JOIN create_event_unrestricted ON (divulgence_events.contract_id = create_event_unrestricted.contract_id),
|
||||||
|
parameters
|
||||||
|
WHERE divulgence_events.contract_id = $contractId -- restrict to aid query planner
|
||||||
|
AND divulgence_events.event_sequential_id <= parameters.ledger_end_sequential_id
|
||||||
|
AND $treeEventWitnessesClause
|
||||||
|
ORDER BY divulgence_events.event_sequential_id
|
||||||
|
-- prudent engineering: make results more stable by preferring earlier divulgence events
|
||||||
|
-- Results might still change due to pruning.
|
||||||
|
FETCH NEXT 1 ROW ONLY
|
||||||
|
),
|
||||||
|
create_and_divulged_contracts AS (
|
||||||
|
(SELECT * FROM create_event) -- prefer create over divulgence events
|
||||||
|
UNION ALL
|
||||||
|
(SELECT * FROM divulged_contract)
|
||||||
|
)
|
||||||
|
SELECT contract_id, #${resultColumns.mkString(", ")}
|
||||||
|
FROM create_and_divulged_contracts
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM archival_event)
|
||||||
|
FETCH NEXT 1 ROW ONLY"""
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -170,8 +170,10 @@ private[backend] object OracleStorageBackend
|
|||||||
|
|
||||||
override def eventStrategy: common.EventStrategy = OracleEventStrategy
|
override def eventStrategy: common.EventStrategy = OracleEventStrategy
|
||||||
|
|
||||||
// TODO FIXME: confirm this works for oracle
|
// TODO FIXME: Use tables directly instead of the participant_events view.
|
||||||
def maxEventSeqIdForOffset(offset: Offset)(connection: Connection): Option[Long] = {
|
def maxEventSequentialIdOfAnObservableEvent(
|
||||||
|
offset: Offset
|
||||||
|
)(connection: Connection): Option[Long] = {
|
||||||
import com.daml.platform.store.Conversions.OffsetToStatement
|
import com.daml.platform.store.Conversions.OffsetToStatement
|
||||||
// This query could be: "select max(event_sequential_id) from participant_events where event_offset <= ${range.endInclusive}"
|
// This query could be: "select max(event_sequential_id) from participant_events where event_offset <= ${range.endInclusive}"
|
||||||
// however tests using PostgreSQL 12 with tens of millions of events have shown that the index
|
// however tests using PostgreSQL 12 with tens of millions of events have shown that the index
|
||||||
|
@ -202,7 +202,10 @@ private[backend] object PostgresStorageBackend
|
|||||||
|
|
||||||
override def eventStrategy: common.EventStrategy = PostgresEventStrategy
|
override def eventStrategy: common.EventStrategy = PostgresEventStrategy
|
||||||
|
|
||||||
override def maxEventSeqIdForOffset(offset: Offset)(connection: Connection): Option[Long] = {
|
// TODO FIXME: Use tables directly instead of the participant_events view.
|
||||||
|
override def maxEventSequentialIdOfAnObservableEvent(
|
||||||
|
offset: Offset
|
||||||
|
)(connection: Connection): Option[Long] = {
|
||||||
import com.daml.platform.store.Conversions.OffsetToStatement
|
import com.daml.platform.store.Conversions.OffsetToStatement
|
||||||
// This query could be: "select max(event_sequential_id) from participant_events where event_offset <= ${range.endInclusive}"
|
// This query could be: "select max(event_sequential_id) from participant_events where event_offset <= ${range.endInclusive}"
|
||||||
// however tests using PostgreSQL 12 with tens of millions of events have shown that the index
|
// however tests using PostgreSQL 12 with tens of millions of events have shown that the index
|
||||||
|
@ -36,6 +36,15 @@ private[sandboxnext] object Cli extends SandboxCli {
|
|||||||
s"Deprecated: Use the Daml Driver for PostgreSQL if you need persistence.\nThe JDBC connection URL to a Postgres database containing the username and password as well. If present, $Name will use the database to persist its data."
|
s"Deprecated: Use the Daml Driver for PostgreSQL if you need persistence.\nThe JDBC connection URL to a Postgres database containing the username and password as well. If present, $Name will use the database to persist its data."
|
||||||
)
|
)
|
||||||
.action((url, config) => config.copy(jdbcUrl = Some(url)))
|
.action((url, config) => config.copy(jdbcUrl = Some(url)))
|
||||||
|
|
||||||
|
parser
|
||||||
|
.opt[Int]("database-connection-pool-size")
|
||||||
|
.optional()
|
||||||
|
.text(
|
||||||
|
s"The number of connections in the database connection pool used for serving ledger API requests."
|
||||||
|
)
|
||||||
|
.action((poolSize, config) => config.copy(databaseConnectionPoolSize = poolSize))
|
||||||
|
|
||||||
parser
|
parser
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,8 +240,7 @@ class Runner(config: SandboxConfig) extends ResourceOwner[Port] {
|
|||||||
port = currentPort.getOrElse(config.port),
|
port = currentPort.getOrElse(config.port),
|
||||||
address = config.address,
|
address = config.address,
|
||||||
jdbcUrl = indexJdbcUrl,
|
jdbcUrl = indexJdbcUrl,
|
||||||
// 16 DB connections has been shown to be sufficient for applications running on the sandbox
|
databaseConnectionPoolSize = config.databaseConnectionPoolSize,
|
||||||
databaseConnectionPoolSize = 16,
|
|
||||||
databaseConnectionTimeout = config.databaseConnectionTimeout,
|
databaseConnectionTimeout = config.databaseConnectionTimeout,
|
||||||
tlsConfig = config.tlsConfig,
|
tlsConfig = config.tlsConfig,
|
||||||
maxInboundMessageSize = config.maxInboundMessageSize,
|
maxInboundMessageSize = config.maxInboundMessageSize,
|
||||||
|
Loading…
Reference in New Issue
Block a user