|
1 | 1 | package no.nav.paw.arbeidssoekerregisteret.topology
|
2 | 2 |
|
3 | 3 | import io.micrometer.core.instrument.MeterRegistry
|
4 |
| -import no.nav.paw.arbeidssoekerregisteret.config.tellAntallMottatteOpplysninger |
| 4 | +import no.nav.paw.arbeidssoekerregisteret.config.antallLagredeOpplysningerSumPerPeriode |
| 5 | +import no.nav.paw.arbeidssoekerregisteret.config.antallLagredeOpplysningerTotal |
| 6 | +import no.nav.paw.arbeidssoekerregisteret.config.buildOpplysningerOmArbeidssoekerAvroSerde |
| 7 | +import no.nav.paw.arbeidssoekerregisteret.config.tellMottatteOpplysninger |
5 | 8 | import no.nav.paw.arbeidssoekerregisteret.context.ApplicationContext
|
6 | 9 | import no.nav.paw.arbeidssokerregisteret.api.v4.OpplysningerOmArbeidssoeker
|
| 10 | +import no.nav.paw.config.kafka.streams.Punctuation |
| 11 | +import no.nav.paw.config.kafka.streams.genericProcess |
| 12 | +import org.apache.kafka.common.serialization.Serdes |
7 | 13 | import org.apache.kafka.streams.StreamsBuilder
|
8 | 14 | import org.apache.kafka.streams.Topology
|
| 15 | +import org.apache.kafka.streams.kstream.Consumed |
| 16 | +import org.apache.kafka.streams.processor.PunctuationType |
| 17 | +import org.apache.kafka.streams.state.Stores |
| 18 | +import org.apache.kafka.streams.state.TimestampedKeyValueStore |
| 19 | +import org.apache.kafka.streams.state.ValueAndTimestamp |
| 20 | +import java.time.Instant |
| 21 | +import java.util.* |
| 22 | +import java.util.concurrent.atomic.AtomicLong |
9 | 23 |
|
10 | 24 | context(ApplicationContext)
|
11 | 25 | fun buildOpplysningerTopology(
|
12 | 26 | meterRegistry: MeterRegistry
|
13 | 27 | ): Topology = StreamsBuilder().apply {
|
| 28 | + addOpplysningerStateStore() |
| 29 | + addOpplysningerKStream(meterRegistry) |
| 30 | +}.build() |
| 31 | + |
| 32 | +context(ApplicationContext) |
| 33 | +private fun StreamsBuilder.addOpplysningerStateStore() { |
| 34 | + logger.info("Oppretter state store for opplysninger om arbeidssøker") |
| 35 | + val kafkaStreamsProperties = properties.kafkaStreams |
| 36 | + |
| 37 | + this.addStateStore( |
| 38 | + Stores.timestampedKeyValueStoreBuilder( |
| 39 | + Stores.persistentKeyValueStore(kafkaStreamsProperties.opplysningerStore), |
| 40 | + Serdes.Long(), |
| 41 | + buildOpplysningerOmArbeidssoekerAvroSerde() |
| 42 | + ) |
| 43 | + ) |
| 44 | +} |
| 45 | + |
| 46 | +context(ApplicationContext) |
| 47 | +private fun StreamsBuilder.addOpplysningerKStream(meterRegistry: MeterRegistry) { |
14 | 48 | logger.info("Oppretter KStream for opplysninger om arbeidssøker")
|
15 | 49 | val kafkaStreamsProperties = properties.kafkaStreams
|
16 | 50 |
|
17 |
| - this.stream<Long, OpplysningerOmArbeidssoeker>(kafkaStreamsProperties.opplysningerTopic) |
| 51 | + this |
| 52 | + .stream( |
| 53 | + kafkaStreamsProperties.opplysningerTopic, |
| 54 | + Consumed.with(Serdes.Long(), buildOpplysningerOmArbeidssoekerAvroSerde()) |
| 55 | + ) |
18 | 56 | .peek { key, _ ->
|
19 | 57 | logger.debug("Mottok event på {} med key {}", kafkaStreamsProperties.opplysningerTopic, key)
|
20 |
| - meterRegistry.tellAntallMottatteOpplysninger() |
| 58 | + meterRegistry.tellMottatteOpplysninger() |
| 59 | + }.genericProcess<Long, OpplysningerOmArbeidssoeker, Long, OpplysningerOmArbeidssoeker>( |
| 60 | + name = "processOpplysningerOmArbeidssoeker", |
| 61 | + stateStoreNames = arrayOf(kafkaStreamsProperties.opplysningerStore), |
| 62 | + punctuation = buildPunctuation(meterRegistry) |
| 63 | + ) { record -> |
| 64 | + val stateStore: TimestampedKeyValueStore<Long, OpplysningerOmArbeidssoeker> = |
| 65 | + getStateStore(kafkaStreamsProperties.opplysningerStore) |
| 66 | + logger.debug("Lagrer opplysninger for periode {}", record.value().periodeId) |
| 67 | + stateStore.put(record.key(), ValueAndTimestamp.make(record.value(), Instant.now().toEpochMilli())) |
21 | 68 | }
|
22 |
| -}.build() |
| 69 | +} |
| 70 | + |
| 71 | +context(ApplicationContext) |
| 72 | +private fun buildPunctuation(meterRegistry: MeterRegistry): Punctuation<Long, OpplysningerOmArbeidssoeker> { |
| 73 | + logger.info("Oppretter Punctuation for opplysninger om arbeidssøker") |
| 74 | + val kafkaStreamsProperties = properties.kafkaStreams |
| 75 | + |
| 76 | + return Punctuation( |
| 77 | + kafkaStreamsProperties.opplysningerPunctuatorSchedule, PunctuationType.WALL_CLOCK_TIME |
| 78 | + ) { timestamp, context -> |
| 79 | + logger.info("Punctuation kjører for tidspunkt {}", timestamp) |
| 80 | + |
| 81 | + with(context) { |
| 82 | + val antallTotalt = AtomicLong(0) |
| 83 | + val histogram = mutableMapOf<UUID, AtomicLong>() |
| 84 | + |
| 85 | + val stateStore: TimestampedKeyValueStore<Long, OpplysningerOmArbeidssoeker> = |
| 86 | + getStateStore(kafkaStreamsProperties.opplysningerStore) |
| 87 | + for (keyValue in stateStore.all()) { |
| 88 | + antallTotalt.incrementAndGet() |
| 89 | + val lagretTidspunkt = Instant.ofEpochMilli(keyValue.value.timestamp()) |
| 90 | + val utloepTidspunkt = Instant.now().minus(kafkaStreamsProperties.opplysningerLagretTidsperiode) |
| 91 | + if (utloepTidspunkt.isAfter(lagretTidspunkt) |
| 92 | + ) { |
| 93 | + logger.debug( |
| 94 | + "Sletter opplysninger for periode {} fordi de har vært lagret mer enn {}m (utløp {} > lagret {})", |
| 95 | + keyValue.value.value().periodeId, |
| 96 | + kafkaStreamsProperties.opplysningerLagretTidsperiode.toMinutes(), |
| 97 | + utloepTidspunkt, |
| 98 | + lagretTidspunkt |
| 99 | + ) |
| 100 | + stateStore.delete(keyValue.key) |
| 101 | + continue |
| 102 | + } |
| 103 | + |
| 104 | + val opplysninger = keyValue.value.value() |
| 105 | + val antall = histogram[opplysninger.periodeId] |
| 106 | + if (antall != null) { |
| 107 | + antall.incrementAndGet() |
| 108 | + histogram[opplysninger.periodeId] = antall |
| 109 | + } else { |
| 110 | + histogram[opplysninger.periodeId] = AtomicLong(1) |
| 111 | + } |
| 112 | + } |
| 113 | + |
| 114 | + histogram.forEach { (_, antall) -> meterRegistry.antallLagredeOpplysningerSumPerPeriode(timestamp, antall) } |
| 115 | + meterRegistry.antallLagredeOpplysningerTotal(antallTotalt) |
| 116 | + } |
| 117 | + } |
| 118 | +} |
0 commit comments