Skip to content

Commit e3022c4

Browse files
committed
best case
1 parent 2920f55 commit e3022c4

File tree

2 files changed

+70
-13
lines changed

2 files changed

+70
-13
lines changed

modules/core/src/main/scala/com.snowplowanalytics.snowplow.snowflake/Metrics.scala

+24-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ trait Metrics[F[_]] {
2121
def addGood(count: Int): F[Unit]
2222
def addBad(count: Int): F[Unit]
2323
def setLatencyMillis(latencyMillis: Long): F[Unit]
24+
def setLatencyCollectorToTargetMillis(latencyMillis: Long): F[Unit]
25+
def setLatencyCollectorToTargetPessimisticMillis(latencyMillis: Long): F[Unit]
2426

2527
def report: Stream[F, Nothing]
2628
}
@@ -33,18 +35,22 @@ object Metrics {
3335
private case class State(
3436
good: Int,
3537
bad: Int,
36-
latencyMillis: Long
38+
latencyMillis: Long,
39+
latencyCollectorToTargetMillis: Long,
40+
latencyCollectorToTargetPessimisticMillis: Long
3741
) extends CommonMetrics.State {
3842
def toKVMetrics: List[CommonMetrics.KVMetric] =
3943
List(
4044
KVMetric.CountGood(good),
4145
KVMetric.CountBad(bad),
42-
KVMetric.LatencyMillis(latencyMillis)
46+
KVMetric.LatencyMillis(latencyMillis),
47+
KVMetric.LatencyCollectorToTargetMillis(latencyCollectorToTargetMillis),
48+
KVMetric.LatencyCollectorToTargetPessimisticMillis(latencyCollectorToTargetPessimisticMillis)
4349
)
4450
}
4551

4652
private object State {
47-
def empty: State = State(0, 0, 0L)
53+
def empty: State = State(0, 0, 0L, 0L, 0L)
4854
}
4955

5056
private def impl[F[_]: Async](config: Config.Metrics, ref: Ref[F, State]): Metrics[F] =
@@ -55,6 +61,10 @@ object Metrics {
5561
ref.update(s => s.copy(bad = s.bad + count))
5662
def setLatencyMillis(latencyMillis: Long): F[Unit] =
5763
ref.update(s => s.copy(latencyMillis = s.latencyMillis.max(latencyMillis)))
64+
def setLatencyCollectorToTargetMillis(latencyMillis: Long): F[Unit] =
65+
ref.update(s => s.copy(latencyMillis = s.latencyMillis.min(latencyMillis)))
66+
def setLatencyCollectorToTargetPessimisticMillis(latencyMillis: Long): F[Unit] =
67+
ref.update(s => s.copy(latencyMillis = s.latencyMillis.max(latencyMillis)))
5868
}
5969

6070
private object KVMetric {
@@ -77,5 +87,16 @@ object Metrics {
7787
val metricType = CommonMetrics.MetricType.Gauge
7888
}
7989

90+
final case class LatencyCollectorToTargetMillis(v: Long) extends CommonMetrics.KVMetric {
91+
val key = "latency_collector_to_target_millis"
92+
val value = v.toString
93+
val metricType = CommonMetrics.MetricType.Gauge
94+
}
95+
96+
final case class LatencyCollectorToTargetPessimisticMillis(v: Long) extends CommonMetrics.KVMetric {
97+
val key = "latency_collector_to_target_pessimistic_millis"
98+
val value = v.toString
99+
val metricType = CommonMetrics.MetricType.Gauge
100+
}
80101
}
81102
}

modules/core/src/main/scala/com.snowplowanalytics.snowplow.snowflake/processing/Processing.scala

+46-10
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ import org.typelevel.log4cats.Logger
2121
import org.typelevel.log4cats.slf4j.Slf4jLogger
2222

2323
import java.nio.charset.StandardCharsets
24-
import java.time.OffsetDateTime
25-
24+
import java.time.{Instant, OffsetDateTime}
2625
import com.snowplowanalytics.iglu.schemaddl.parquet.Caster
2726
import com.snowplowanalytics.snowplow.analytics.scalasdk.Event
2827
import com.snowplowanalytics.snowplow.badrows.{BadRow, Payload => BadPayload, Processor => BadRowProcessor}
@@ -86,7 +85,9 @@ object Processing {
8685
origBatchBytes: Long,
8786
origBatchCount: Int,
8887
badAccumulated: ListOfList[BadRow],
89-
tokens: Vector[Unique.Token]
88+
tokens: Vector[Unique.Token],
89+
maxCollectorTstamp: Instant,
90+
minCollectorTstamp: Instant
9091
)
9192

9293
/**
@@ -246,6 +247,18 @@ object Processing {
246247
}
247248
}
248249

250+
private def setLatencyCollectorToTargetMillis[F[_]: Sync](
251+
metrics: Metrics[F],
252+
collectorTstamp: Instant,
253+
optimistic: Boolean
254+
): F[Unit] =
255+
for {
256+
now <- Sync[F].realTime
257+
latencyMillis = now.toMillis - collectorTstamp.toEpochMilli
258+
_ <- if (optimistic) metrics.setLatencyCollectorToTargetMillis(latencyMillis)
259+
else metrics.setLatencyCollectorToTargetPessimisticMillis(latencyMillis)
260+
} yield ()
261+
249262
/**
250263
* First attempt to write events with the Snowflake SDK
251264
*
@@ -258,8 +271,14 @@ object Processing {
258271
batch: BatchAfterTransform
259272
): F[BatchAfterTransform] =
260273
withWriteAttempt(env, batch) { notWritten =>
274+
val setLatency =
275+
if (notWritten.isEmpty) {
276+
setLatencyCollectorToTargetMillis(env.metrics, batch.maxCollectorTstamp, optimistic = true) >>
277+
setLatencyCollectorToTargetMillis(env.metrics, batch.minCollectorTstamp, optimistic = false)
278+
} else Sync[F].unit
261279
val parsedResult = ParsedWriteResult.buildFrom(batch.toBeInserted, notWritten)
262280
for {
281+
_ <- setLatency
263282
_ <- abortIfFatalException[F](parsedResult.unexpectedFailures)
264283
_ <- handleSchemaEvolution(env, parsedResult.extraColsRequired)
265284
} yield {
@@ -285,13 +304,18 @@ object Processing {
285304
batch: BatchAfterTransform
286305
): F[BatchAfterTransform] =
287306
withWriteAttempt(env, batch) { notWritten =>
307+
val setLatency =
308+
if (notWritten.isEmpty) {
309+
setLatencyCollectorToTargetMillis(env.metrics, batch.maxCollectorTstamp, optimistic = true) >>
310+
setLatencyCollectorToTargetMillis(env.metrics, batch.minCollectorTstamp, optimistic = false)
311+
} else Sync[F].unit
288312
val mapped = notWritten match {
289313
case Nil => Nil
290314
case more =>
291315
val indexed = batch.toBeInserted.copyToIndexedSeq
292316
more.map(f => (fastGetByIndex(indexed, f.index)._1, f.cause))
293317
}
294-
abortIfFatalException[F](mapped).as {
318+
setLatency >> abortIfFatalException[F](mapped).as {
295319
val moreBad = mapped.map { case (event, sfe) =>
296320
badRowFromEnqueueFailure(badProcessor, event, sfe)
297321
}
@@ -385,15 +409,25 @@ object Processing {
385409

386410
private def fastGetByIndex[A](items: IndexedSeq[A], index: Long): A = items(index.toInt)
387411

412+
private implicit class MaxCollectorTstamp(events: List[EventWithTransform]) {
413+
def maxCollectorTstamp(): Instant = events.map(_._1).map(_.collector_tstamp).max
414+
}
415+
416+
private implicit class MinCollectorTstamp(events: List[EventWithTransform]) {
417+
def minCollectorTstamp(): Instant = events.map(_._1).map(_.collector_tstamp).min
418+
}
419+
388420
private implicit def batchable: BatchUp.Batchable[TransformedBatch, BatchAfterTransform] =
389421
new BatchUp.Batchable[TransformedBatch, BatchAfterTransform] {
390422
def combine(b: BatchAfterTransform, a: TransformedBatch): BatchAfterTransform =
391423
BatchAfterTransform(
392-
toBeInserted = b.toBeInserted.prepend(a.events),
393-
origBatchBytes = b.origBatchBytes + a.countBytes,
394-
origBatchCount = b.origBatchCount + a.countItems,
395-
badAccumulated = b.badAccumulated.prepend(a.parseFailures).prepend(a.transformFailures),
396-
tokens = b.tokens :+ a.token
424+
toBeInserted = b.toBeInserted.prepend(a.events),
425+
origBatchBytes = b.origBatchBytes + a.countBytes,
426+
origBatchCount = b.origBatchCount + a.countItems,
427+
badAccumulated = b.badAccumulated.prepend(a.parseFailures).prepend(a.transformFailures),
428+
tokens = b.tokens :+ a.token,
429+
maxCollectorTstamp = List(b.maxCollectorTstamp, a.events.maxCollectorTstamp()).max,
430+
minCollectorTstamp = List(b.maxCollectorTstamp, a.events.maxCollectorTstamp()).min
397431
)
398432

399433
def single(a: TransformedBatch): BatchAfterTransform =
@@ -402,7 +436,9 @@ object Processing {
402436
a.countBytes,
403437
a.countItems,
404438
ListOfList.ofLists(a.parseFailures, a.transformFailures),
405-
Vector(a.token)
439+
Vector(a.token),
440+
a.events.maxCollectorTstamp(),
441+
a.events.minCollectorTstamp()
406442
)
407443

408444
def weightOf(a: TransformedBatch): Long =

0 commit comments

Comments
 (0)