Prosesseringsmotor for familieområdet.
- Oppdater status i task-tabellen til lengde 20:
ALTER TABLE task ALTER COLUMN status VARCHAR(20) DEFAULT 'UBEHANDLET'::CHARACTER VARYING NOT NULL;
I prosessering oppretter man en task (en jobb).
Hver task har en type, som gjør at prosessering kan bruke riktig AsyncTaskStep for å håndeter akkurat den tasken.
En task lagres i databasen og blir plukket opp av biblioteket, og kaller riktig AsyncTaskStep for den typen.
@Service
class Foo(
val taskService: TaskService
) {
fun opprettOppgave(data: Data) {
taskService.save(FooTask.opprettTask(data))
}
}
@Service
@TaskStepBeskrivelse(
taskStepType = FooTask.TYPE,
maxAntallFeil = 3,
settTilManuellOppfølgning = true,
triggerTidVedFeilISekunder = 60L,
beskrivelse = "Oppretter oppgave for julenissen",
)
class FooTask(
private val oppgaveService: OppgaveService,
) : AsyncTaskStep {
override fun doTask(task: Task) {
oppgaveService.opprettOppgave(...)
//...
}
companion object {
const val TYPE = "Foo"
fun opprettTask(data: Data): Task {
// payload er en streng, men kan også være en serialisert json,
// som då kan plukkes opp i doTask
return Task(type = TYPE, payload = "", properties = Properties())
.medTriggerTid(LocalDateTime.now())
}
}
}- Tasken rekjøres
maxAntallFeilganger før den settes tilFEILETellerMANUELL_OPPFØLGING
- Unikt tasknavn som lagres ned i databasen (
Task.type) og brukes for å finne riktig klasse som skal utføre tasken.
- Enkel beskrivelse av hva tasken er ment å utføre.
- Dersom tasken feiler vil den vente i
triggerTidVedFeilISekundersekunder før den (tidligst) prøver å rekjøre neste gang
- Dersom tasken feiler og ikke lenger skal rekjøres får den status
FEILETsom utgangspunkt. Da vil den bli forsøkt rekjørt neste dag, men for noen tasker må det en manuell prosess til før noe går igjennom. Da kan man settesettTilManuellOppfølgningså vil den gå direkte tilMANUELL_OPPFØLGINGistedenforFEILET
- TaskStepExecutorService plukker opp tasks som skal prosesseres, basert på status og trigger-tid, og køer opp de .
Håndtering av tasks fra køen
ThreadPoolTaskExecutortar en task fra køen- Setter LogContext på tråden, setter
callIdiMDCsånn at man får sporet all logg for den tasken - Markerer den som plukket sånn at ikke andre tråder eller podder plukker samme task
- Sender tasken til
TaskWorkerfor prosessering av tasken- Ved en ev. feil kalles
doFeilhåndtering
- Ved en ev. feil kalles
TaskWorker har metoder for prosessering av tasken
- Setter status
BEHANDLERpå tasken - Finner riktig
AsyncTaskStepfor gitt tasktype og kaller på de ulike metodene for å behandle tasken - Setter status
FERDIGpå tasken - PS: Siden
transactionalbenyttes, så vil aldri tasken ha statusBEHANDLERi databasen. Den går rett fraPLUKKETtilFERDIG
- Lagrer en TaskLogg om at tasken har feilet
Hvis en task ikke har feilet flere ganger enn hva som er definiert som maks antall feil i TaskStepBeskrivelse,
så vil tasken få ny status KLAR_TIL_PLUKK og bli rekjørt senere.
Hver task får en callId som settes settes i MDC for å kunne spore logger og kall for den tasken.
Dersom man har en task som skal opprette flere nye tasks, så kan det ofte være hensiktsmessig å gi hver ny task en ny callId, for å enklere kunne spore hver task. For å gjøre dette må man erstatte properties etter at man opprettet tasken fordi konstruktorn alltid overskriver callId.
val properties = Properties().apply {
setProperty("behandlingId", behandlingId.toString())
setProperty(MDCConstants.MDC_CALL_ID, IdUtils.generateId())
}
return Task(type = TYPE, payload = "", properties = Properties())
.copy(metadataWrapper = PropertiesWrapper(properties))Hvis man ønsker å kaste en exception uten noen stacktrace fra sin AsyncTaskStep så kan man kaste en
TaskExceptionUtenStackTrace som ikke gir en lang stack trace i prosessering-GUI'et når man ser på en task.
I noen tilfeller ønsker man å rekjøre tasken senere og ikke med en gang. Da kan tasken kaste denne feilen og spesifisere triggertid for når den skal kjøres neste gang.
Denne skal ikke kastes, men er egentlig en dto for å serialisere og dokumentere i TaskLogg at en task har feilet og nådd maks antall rekjøringer.
Alle properties har defaultverdier.
prosessering:
# mulighet får å skru av prosessering
enabled: true
# antall tasks som prosesseres samtidig
maxAntall: 10
# Antall ms mellom hver polling av tasks fra databasen
fixedDelayString.in.milliseconds: 30000
# antall tasks som plukkes opp fra databasen
queue.capacity: 20
# antall parallelle tasks som kjøres
pool.size: 4
# Se egen dokumentasjon om continuousRunning
continuousRunning.enabled: false
# sletter historiske tasks - med status ferdig
delete
# antall uker
after.weeks: 2
# antall tasks som plukkes opp for sletting
pagesize: 10000
# tidspunkt for når jobb som rekjører feilede tasks skal kjøre
cronRetryTasks: 0 0 7 1/1 * ?Tasks blir plukket opp hver fixedDelayString.in.milliseconds ms.
Når tasks er plukket og kjørt ferdig, så venter systemet fixedDelayString.in.milliseconds til neste plukk av tasker.
Hvis continuousRunning.enabled settes til true, så poller systemet umiddelbart etter kjøring av de forrige taskene.
- Må sette opp converters, eks:
@Configuration
class DatabaseConfiguration : AbstractJdbcConfiguration() {
@Bean
override fun jdbcCustomConversions(): JdbcCustomConversions {
return JdbcCustomConversions(listOf(StringTilPropertiesWrapperConverter(),
PropertiesWrapperTilStringConverter()))
}
}For å kunne fjerne avhengigheter til felles familie-repoer må man selv konfigurere en bean for ProsesseringInfoProvider
Eksempel på implementasjon, husk at SikkerhetsContext.hentSaksbehandlerNavn() ofte henter name fra claims og ikke preferred_username
@Bean
fun prosesseringInfoProvider() = object : ProsesseringInfoProvider {
override fun hentBrukernavn(): String = try {
SpringTokenValidationContextHolder().tokenValidationContext.getClaims("azuread").getStringClaim("preferred_username")
} catch (e: Exception) {
SikkerhetContext.SYSTEM_FORKORTELSE
}
override fun harTilgang(): Boolean = grupper().contains("<id for AdGruppe som skal ha tilgang>")
private fun grupper(): List<String> {
return try {
SpringTokenValidationContextHolder().tokenValidationContext.getClaims("azuread")
?.get("groups") as List<String>? ?: emptyList()
} catch (e: Exception) {
emptyList()
}
}
override fun isLeaader(): Boolean = LeaderClient.isLeader() ?: true
}- prosessering-web-spring-security
- prosessering-web-nav-token-support Web-laget er trukket ut i egne moduler. Det er opp til konsumenter av familie-prosessering hvilken av disse de skal bruke. Token-validering blir gjort automatisk med disse. Les mer spesifikt i de tilhørende modulene.