Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ object Migration extends MdcLoggable {
alterMappedCustomerAttribute(startedBeforeSchemifier)
dropMappedBadLoginAttemptIndex()
alterMetricColumnUrlLength()
alterMetricArchiveColumnCorrelationidLength()
// populateViewDefinitionCanAddTransactionRequestToBeneficiary()
// populateViewDefinitionCanSeeTransactionStatus()
alterCounterpartyLimitFieldType()
Expand Down Expand Up @@ -493,6 +494,13 @@ object Migration extends MdcLoggable {
}
}

private def alterMetricArchiveColumnCorrelationidLength(): Boolean = {
val name = nameOf(alterMetricArchiveColumnCorrelationidLength)
runOnce(name) {
MigrationOfMetricArchiveTable.alterColumnCorrelationidLength(name)
}
}

private def dropConsentAuthContextDropIndex(): Boolean = {
val name = nameOf(dropConsentAuthContextDropIndex)
runOnce(name) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package code.api.util.migration

import java.time.format.DateTimeFormatter
import java.time.{ZoneId, ZonedDateTime}

import code.api.util.APIUtil
import code.api.util.migration.Migration.{DbFunction, saveLog}
import code.metrics.MetricArchive
import net.liftweb.common.Full
import net.liftweb.mapper.Schemifier

/**
* Widen `metricarchive.correlationid` from varchar(36) to varchar(256) so it matches
* the live `metric` table (which was already widened by
* [[MigrationOfMetricTable.alterColumnCorrelationidLength]]).
*
* Why this is needed: the correlation id is whatever the caller sends in `X-Request-ID`
* (mandatory for Berlin Group / PSD2, optional elsewhere) — a free-form, client-controlled
* string up to 256 chars, not a UUID. The archive column was modelled as a MappedUUID
* (varchar 36), so the MetricsArchiveScheduler failed on the first row whose correlation id
* exceeded 36 chars with `value too long for type character varying(36)`. Because the
* archiver moves rows oldest-first, the same un-archivable rows were retried every run, so
* no archive run ever succeeded. Lift's Schemifier never alters an existing column's width,
* so this explicit migration is required on already-provisioned databases.
*/
object MigrationOfMetricArchiveTable {

val oneDayAgo = ZonedDateTime.now(ZoneId.of("UTC")).minusDays(1)
val oneYearInFuture = ZonedDateTime.now(ZoneId.of("UTC")).plusYears(1)
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm'Z'")

def alterColumnCorrelationidLength(name: String): Boolean = {
DbFunction.tableExists(MetricArchive)
match {
case true =>
val startDate = System.currentTimeMillis()
val commitId: String = APIUtil.gitCommit
var isSuccessful = false

val executedSql =
DbFunction.maybeWrite(true, Schemifier.infoF _) {
APIUtil.getPropsValue("db.driver") match {
case Full(dbDriver) if dbDriver.contains("com.microsoft.sqlserver.jdbc.SQLServerDriver") =>
() =>
"""
|ALTER TABLE metricarchive ALTER COLUMN correlationid varchar(256);
|""".stripMargin
case _ =>
() =>
"""
|ALTER TABLE metricarchive ALTER COLUMN correlationid TYPE character varying(256);
|""".stripMargin
}
}

val endDate = System.currentTimeMillis()
val comment: String =
s"""Executed SQL:
|$executedSql
|""".stripMargin
isSuccessful = true
saveLog(name, commitId, isSuccessful, startDate, endDate, comment)
isSuccessful

case false =>
val startDate = System.currentTimeMillis()
val commitId: String = APIUtil.gitCommit
val isSuccessful = false
val endDate = System.currentTimeMillis()
val comment: String =
s"""${MetricArchive._dbTableNameLC} table does not exist""".stripMargin
saveLog(name, commitId, isSuccessful, startDate, endDate, comment)
isSuccessful
}
}
}
14 changes: 13 additions & 1 deletion obp-api/src/main/scala/code/metrics/MappedMetrics.scala
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,12 @@ class MappedMetric extends APIMetric with LongKeyedMapper[MappedMetric] with IdP
//(GET, POST etc.) --S.request.get.requestType
object verb extends MappedString(this, 16)
object httpCode extends MappedInt(this)
// NOT necessarily a UUID, despite the generateUUID default. When the caller sends
// an `X-Request-ID` header (mandatory for Berlin Group / PSD2, optional elsewhere)
// that value is adopted verbatim as the correlation id (see APIUtil.scala ~2959),
// echoed back as the `Correlation-Id` response header. It is client-controlled and
// free-form on non-Berlin-Group paths, hence the generous 256 width. The archive
// copy (MetricArchive.correlationId) MUST keep the same width or the archiver fails.
object correlationId extends MappedString(this, 256) {
override def dbNotNull_? = true
override def defaultValue = generateUUID()
Expand Down Expand Up @@ -725,7 +731,13 @@ class MetricArchive extends APIMetric with LongKeyedMapper[MetricArchive] with I
//(GET, POST etc.) --S.request.get.requestType
object verb extends MappedString(this, 16)
object httpCode extends MappedInt(this)
object correlationId extends MappedUUID(this){
// Must mirror the source Metric.correlationId width (256), NOT a UUID's 36. The
// live correlation id is a free string (client-supplied or upstream trace id) that
// routinely exceeds 36 chars; as a MappedUUID (varchar 36) this column rejected the
// first such row the archiver copied with "value too long for type character
// varying(36)", failing every run — and since the archiver moves oldest-first, the
// same un-archivable rows were retried forever, so no run ever succeeded.
object correlationId extends MappedString(this, 256){
override def dbNotNull_? = true
}
object responseBody extends MappedText(this)
Expand Down
Loading