Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Innfør støtte for ny feilhåndtering uten behov #471

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class NotifikasjonHentIdLoeser(
)
} else {
"Fant ikke sakId.".also {
logger.error(it)
logger.error(it) // TODO: bare logg en notis?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fint med error her så lenge vi har klassen. Det er strengt tatt en feil vi burde være klar over, selv om vi ikke gjør stort for å rette den opp om dagen.

sikkerLogger.error(it)
}
}
Expand Down
2 changes: 1 addition & 1 deletion feil-behandler/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Dependency versions
bakgrunnsjobbVersion=1.0.4
bakgrunnsjobbVersion=1.0.6
flywayVersion=10.8.1
hikariVersion=5.1.0
postgresqlVersion=42.7.2
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package no.nav.helsearbeidsgiver.inntektsmelding.feilbehandler.river

import kotlinx.serialization.builtins.serializer
import no.nav.hag.utils.bakgrunnsjobb.Bakgrunnsjobb
import no.nav.hag.utils.bakgrunnsjobb.BakgrunnsjobbRepository
import no.nav.hag.utils.bakgrunnsjobb.BakgrunnsjobbStatus
Expand Down Expand Up @@ -30,7 +31,6 @@ class FeilLytter(rapidsConnection: RapidsConnection, private val repository: Bak
BehovType.OPPRETT_SAK,
BehovType.PERSISTER_OPPGAVE_ID,
BehovType.PERSISTER_SAK_ID,
BehovType.JOURNALFOER,
BehovType.LAGRE_JOURNALPOST_ID,
BehovType.NOTIFIKASJON_HENT_ID
)
Expand Down Expand Up @@ -108,8 +108,9 @@ class FeilLytter(rapidsConnection: RapidsConnection, private val repository: Bak
return false
}
val behovFraMelding = fail.utloesendeMelding.toMap()[Key.BEHOV]?.fromJson(BehovType.serializer())
val skalHaandteres = behovSomHaandteres.contains(behovFraMelding)
sikkerLogger.info("Behov: $behovFraMelding skal håndteres: $skalHaandteres")
val harRetry = fail.utloesendeMelding.toMap()[Key.RETRY]?.fromJson(String.serializer())
val skalHaandteres = harRetry != null || behovSomHaandteres.contains(behovFraMelding)
Comment on lines +111 to +112
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hvis du stoler på at Key.RETRY har verdi så kan du erstatte harRetry != null med Key.RETRY in fail.utloesendeMelding.toMap().

sikkerLogger.info("Feil skal håndteres: $skalHaandteres")
return skalHaandteres
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import no.nav.helsearbeidsgiver.felles.json.toJson
import no.nav.helsearbeidsgiver.felles.json.toMap
import no.nav.helsearbeidsgiver.felles.rapidsrivers.model.Fail
import no.nav.helsearbeidsgiver.felles.rapidsrivers.model.ModelUtils.toFailOrNull
import no.nav.helsearbeidsgiver.felles.utils.RetryID
import no.nav.helsearbeidsgiver.inntektsmelding.feilbehandler.prosessor.FeilProsessor
import no.nav.helsearbeidsgiver.utils.json.parseJson
import no.nav.helsearbeidsgiver.utils.json.toJson
Expand All @@ -32,17 +33,21 @@ class FeilLytterTest : FunSpec({
}

test("skal håndtere gyldige feil med spesifiserte behov") {
handler.behovSomHaandteres.forEach { handler.skalHaandteres(lagGyldigFeil(it)) shouldBe true }
handler.behovSomHaandteres.forEach { handler.skalHaandteres(lagGyldigFeilMedBehov(it)) shouldBe true }
}

test("skal ignorere gyldige feil med visse behov") {
val ignorerteBehov = BehovType.entries.filterNot { handler.behovSomHaandteres.contains(it) }
ignorerteBehov.forEach { handler.skalHaandteres(lagGyldigFeil(it)) shouldBe false }
ignorerteBehov.forEach { handler.skalHaandteres(lagGyldigFeilMedBehov(it)) shouldBe false }
}

test("skal håndtere gyldige feil med spesifisert retry") {
RetryID.values().forEach { handler.skalHaandteres(lagGyldigFeil(it)) shouldBe true }
}

test("skal ignorere feil uten behov") {
val uuid = UUID.randomUUID()
val feil = lagGyldigFeil(BehovType.LAGRE_JOURNALPOST_ID).copy(
val feil = lagGyldigFeilMedBehov(BehovType.LAGRE_JOURNALPOST_ID).copy(
utloesendeMelding =
JsonMessage.newMessage(
mapOf(
Expand All @@ -56,21 +61,58 @@ class FeilLytterTest : FunSpec({

test("skal ignorere feil uten forespørselId") {
// TODO: Kan egentlig tillate feil uten forespørselId..
val feil = lagGyldigFeil(BehovType.LAGRE_JOURNALPOST_ID).copy(forespoerselId = null)
val feil = lagGyldigFeilMedBehov(BehovType.LAGRE_JOURNALPOST_ID).copy(forespoerselId = null)
handler.skalHaandteres(feil) shouldBe false
}

test("Ny feil med forskjellig behov og samme id skal lagres") {
val now = LocalDateTime.now()
rapid.sendTestMessage(lagRapidFeilmelding())
rapid.sendTestMessage(lagRapidFeilmeldingMedBehov())
repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.OPPRETTET), true).size shouldBe 1
rapid.sendTestMessage(lagRapidFeilmelding(BehovType.LAGRE_FORESPOERSEL))
rapid.sendTestMessage(lagRapidFeilmeldingMedBehov(BehovType.LAGRE_FORESPOERSEL))
repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.OPPRETTET), true).size shouldBe 2
}

test("Gammel med behov: Duplikatfeil (samme feil etter rekjøring) skal oppdatere eksisterende feil -> status: FEILET") {
val now = LocalDateTime.now()
val feilmelding = lagRapidFeilmeldingMedBehov()
rapid.sendTestMessage(feilmelding)
repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.OPPRETTET), true).size shouldBe 1
rapid.sendTestMessage(feilmelding)
repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.OPPRETTET), true).size shouldBe 0
val oppdatert = repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.FEILET), true)
oppdatert.size shouldBe 1
oppdatert[0].forsoek shouldBe 0 // Antall forsøk oppdateres av bakgrunnsjobbService

rapid.sendTestMessage(feilmelding)
rapid.sendTestMessage(feilmelding)
rapid.sendTestMessage(feilmelding)
val feilet = repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.FEILET), true)
feilet.size shouldBe 1
feilet[0].forsoek shouldBe 0
}

test("Gammel med behov: Skal sette jobb til STOPPET når maks antall forsøk er overskredet") {
val now = LocalDateTime.now()
val feilmelding = lagRapidFeilmeldingMedBehov()
val feil = toFailOrNull(feilmelding.parseJson().toMap())!!

repository.save(
Bakgrunnsjobb(
feil.transaksjonId,
FeilProsessor.JOB_TYPE,
forsoek = 4,
maksAntallForsoek = 3,
data = feil.utloesendeMelding.toString()
)
)
rapid.sendTestMessage(feilmelding)
repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.STOPPET), true).size shouldBe 1
}

test("Duplikatfeil (samme feil etter rekjøring) skal oppdatere eksisterende feil -> status: FEILET") {
val now = LocalDateTime.now()
val feilmelding = lagRapidFeilmelding()
val feilmelding = lagRapidFeilmelding(RetryID.JOURNALFOER)
rapid.sendTestMessage(feilmelding)
repository.findByKjoeretidBeforeAndStatusIn(now.plusMinutes(1), setOf(BakgrunnsjobbStatus.OPPRETTET), true).size shouldBe 1
rapid.sendTestMessage(feilmelding)
Expand All @@ -89,7 +131,7 @@ class FeilLytterTest : FunSpec({

test("Skal sette jobb til STOPPET når maks antall forsøk er overskredet") {
val now = LocalDateTime.now()
val feilmelding = lagRapidFeilmelding()
val feilmelding = lagRapidFeilmelding(RetryID.JOURNALFOER)
val feil = toFailOrNull(feilmelding.parseJson().toMap())!!

repository.save(
Expand All @@ -106,7 +148,7 @@ class FeilLytterTest : FunSpec({
}
})

fun lagRapidFeilmelding(behovType: BehovType = BehovType.LAGRE_JOURNALPOST_ID): String {
fun lagRapidFeilmeldingMedBehov(behovType: BehovType = BehovType.LAGRE_JOURNALPOST_ID): String {
val eventName = EventName.INNTEKTSMELDING_MOTTATT
val transaksjonId = UUID.randomUUID()
val forespoerselId = UUID.randomUUID()
Expand All @@ -129,7 +171,35 @@ fun lagRapidFeilmelding(behovType: BehovType = BehovType.LAGRE_JOURNALPOST_ID):
.toString()
}

fun lagGyldigFeil(behov: BehovType): Fail {
fun lagRapidFeilmelding(retryId: RetryID): String {
val eventName = EventName.INNTEKTSMELDING_MOTTATT
val transaksjonId = UUID.randomUUID()
val forespoerselId = UUID.randomUUID()

return mapOf(
Key.FAIL to Fail(
feilmelding = "Klarte ikke journalføre",
event = eventName,
transaksjonId = transaksjonId,
forespoerselId = forespoerselId,
utloesendeMelding = mapOf(
Key.EVENT_NAME to eventName.toJson(),
Key.RETRY to retryId.toJson(RetryID.serializer()),
Key.FORESPOERSEL_ID to forespoerselId.toJson(),
Key.UUID to transaksjonId.toJson()
).toJson()
).toJson(Fail.serializer())
)
.toJson()
.toString()
}

/*
"Gammel" feil og packet - behov vil i fremtiden kun være satt for "synkrone" kall som forventer et svar.
Feil som inneholder behov vil etter hvert aldri rekjøres, retry-flagg vil erstatte Behov-logikken,
men dette vil endres gradvis, ikke big-bang
*/
fun lagGyldigFeilMedBehov(behov: BehovType): Fail {
val uuid = UUID.randomUUID()
val jsonMessage = JsonMessage.newMessage(
EventName.OPPGAVE_OPPRETT_REQUESTED.name,
Expand All @@ -141,3 +211,19 @@ fun lagGyldigFeil(behov: BehovType): Fail {
)
return Fail("Feil", EventName.OPPGAVE_OPPRETT_REQUESTED, UUID.randomUUID(), UUID.randomUUID(), jsonMessage.toJson().parseJson())
}

/*
Ny feil - som angir med Key.RETRY at den skal forsøkes på nytt
*/
fun lagGyldigFeil(retryId: RetryID): Fail {
val uuid = UUID.randomUUID()
val jsonMessage = JsonMessage.newMessage(
EventName.OPPGAVE_OPPRETT_REQUESTED.name,
mapOf(
Key.RETRY.str to retryId,
Key.UUID.str to uuid,
Key.FORESPOERSEL_ID.str to uuid
)
)
return Fail("Feil", EventName.OPPGAVE_OPPRETT_REQUESTED, UUID.randomUUID(), UUID.randomUUID(), jsonMessage.toJson().parseJson())
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ enum class Key(override val str: String) : IKey {
TILGANG("tilgang"),
SPINN_INNTEKTSMELDING_ID("spinnInntektsmeldingId"),
EKSTERN_INNTEKTSMELDING("eksternInntektsmelding"),
ER_DUPLIKAT_IM("er_duplikat_im");
ER_DUPLIKAT_IM("er_duplikat_im"),
RETRY("retry");

override fun toString(): String =
str
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package no.nav.helsearbeidsgiver.felles.utils

import kotlinx.serialization.Serializable

interface Retriable {

/*
Kunne kanskje bare vært en statisk metode, heller enn interface.
Dersom retryId er null, er det en vanlig pakke og skal behandles
Dersom retryId er satt, må man sjekke om den matcher ens egen listenerId,
bare da skal pakken behandles.
*/
fun erRetryOgMatcherLytteren(myListenerID: RetryID, retryId: RetryID?): Boolean {
return retryId == null || myListenerID == retryId
}
}

@Serializable
enum class RetryID {
JOURNALFOER
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package no.nav.helsearbeidsgiver.inntektsmelding.joark

import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.json.JsonElement
import no.nav.helsearbeidsgiver.dokarkiv.DokArkivClient
import no.nav.helsearbeidsgiver.dokarkiv.domene.Avsender
Expand All @@ -18,6 +19,8 @@ import no.nav.helsearbeidsgiver.felles.metrics.Metrics
import no.nav.helsearbeidsgiver.felles.metrics.recordTime
import no.nav.helsearbeidsgiver.felles.rapidsrivers.model.Fail
import no.nav.helsearbeidsgiver.felles.utils.Log
import no.nav.helsearbeidsgiver.felles.utils.Retriable
import no.nav.helsearbeidsgiver.felles.utils.RetryID
import no.nav.helsearbeidsgiver.utils.collection.mapValuesNotNull
import no.nav.helsearbeidsgiver.utils.json.fromJson
import no.nav.helsearbeidsgiver.utils.json.serializer.UuidSerializer
Expand All @@ -38,16 +41,19 @@ data class JournalfoerImMelding(

class JournalfoerImRiver(
private val dokArkivClient: DokArkivClient
) : ObjectRiver<JournalfoerImMelding>() {
) : ObjectRiver<JournalfoerImMelding>(), Retriable {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jeg ville foretrukket å ikke ha dette interfacet, ettersom man helt fint kan være Retriable, men faktisk ikke akseptere meldinger med retry. Da blir det forvirrende.


private val logger = logger()
private val sikkerLogger = sikkerLogger()
private val LISTENER_ID = RetryID.JOURNALFOER
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LISTENER_ID -> riverId?
Siden vi har rivers, som er en slags subklasse av listeners. Og camelCase siden verdien ikke er en const.


override fun les(json: Map<Key, JsonElement>): JournalfoerImMelding? {
val behovType = Key.BEHOV.lesOrNull(BehovType.serializer(), json)
val retry = Key.RETRY.lesOrNull(RetryID.serializer(), json)
return if (
setOf(Key.DATA, Key.FAIL).any(json::containsKey) ||
(behovType != null && behovType != BehovType.JOURNALFOER)
(behovType != null && behovType != BehovType.JOURNALFOER) || // TODO: kan fjernes
(!erRetryOgMatcherLytteren(LISTENER_ID, retry))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Foreslår å bytte ut !erRetryOgMatcherLytteren(LISTENER_ID, retry) med retry.isNullOrNotValue(LISTENER_ID).

) {
null
} else {
Expand Down Expand Up @@ -120,7 +126,7 @@ class JournalfoerImRiver(
transaksjonId = transaksjonId,
forespoerselId = json[Key.FORESPOERSEL_ID]?.fromJson(UuidSerializer),
utloesendeMelding = json.plus(
Key.BEHOV to BehovType.JOURNALFOER.toJson()
Key.RETRY to LISTENER_ID.toJson(RetryID.serializer())
)
.toJson()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import no.nav.helsearbeidsgiver.felles.rapidsrivers.model.Fail
import no.nav.helsearbeidsgiver.felles.test.mock.mockInntektsmeldingV1
import no.nav.helsearbeidsgiver.felles.test.rapidsrivers.firstMessage
import no.nav.helsearbeidsgiver.felles.test.rapidsrivers.sendJson
import no.nav.helsearbeidsgiver.felles.utils.RetryID
import no.nav.helsearbeidsgiver.utils.json.serializer.UuidSerializer
import no.nav.helsearbeidsgiver.utils.json.toJson
import java.time.LocalDate
Expand Down Expand Up @@ -240,7 +241,7 @@ class JournalfoerImRiverTest : FunSpec({
transaksjonId = innkommendeMelding.transaksjonId,
forespoerselId = forespoerselId,
utloesendeMelding = innkommendeJsonMap.plus(
Key.BEHOV to BehovType.JOURNALFOER.toJson()
Key.RETRY to RetryID.JOURNALFOER.toJson(RetryID.serializer())
)
.toJson()
)
Expand Down Expand Up @@ -287,7 +288,7 @@ class JournalfoerImRiverTest : FunSpec({
transaksjonId = innkommendeMelding.transaksjonId,
forespoerselId = null,
utloesendeMelding = innkommendeJsonMap.plus(
Key.BEHOV to BehovType.JOURNALFOER.toJson()
Key.RETRY to RetryID.JOURNALFOER.toJson(RetryID.serializer())
)
.toJson()
)
Expand Down