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

Forbedre ytelse på identpool #3647

Merged
merged 10 commits into from
Oct 14, 2024
4 changes: 2 additions & 2 deletions apps/testnav-ident-pool/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ spec:
limits:
memory: 4096Mi
requests:
cpu: 200m
cpu: 600m
memory: 2048Mi
accessPolicy:
inbound:
Expand All @@ -68,7 +68,7 @@ spec:
cluster: dev-gcp
gcp:
sqlInstances:
- type: POSTGRES_15
- type: POSTGRES_16
tier: db-custom-2-7680
name: testnav-identpool
databases:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;

Expand All @@ -19,15 +20,20 @@ public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Excepti
httpSecurity.sessionManagement(sessionConfig -> sessionConfig.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authorizeConfig -> authorizeConfig.requestMatchers(
"/internal/**",
"/webjars/**",
"/swagger-resources/**",
"/v3/api-docs/**",
"/swagger-ui/**",
"/swagger",
"/error",
"/swagger-ui.html"
).permitAll().requestMatchers("/api/**").fullyAuthenticated())
"/internal/**",
"/webjars/**",
"/swagger-resources/**",
"/v3/api-docs/**",
"/swagger-ui/**",
"/swagger",
"/error",
"/swagger-ui.html",
"/h2/**",
"/member/**")
.permitAll()
.requestMatchers("/api/**")
.fullyAuthenticated())
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
.oauth2ResourceServer(oauth2RSConfig -> oauth2RSConfig.jwt(Customizer.withDefaults()));

return httpSecurity.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,40 @@ Page<Ident> findAll(@Param("rekvireringsstatus") Rekvireringsstatus rekvirerings
@Param("identtype") Identtype identtype, @Param("kjoenn") Kjoenn kjoenn,
@Param("foedtFoer") LocalDate foedtFoer, @Param("foedtEtter") LocalDate foedtEtter,
@Param("syntetisk") boolean syntetisk, Pageable pageable);

@Query
int countAllByRekvireringsstatusAndIdenttypeAndSyntetiskAndFoedselsdatoBetween(
Rekvireringsstatus rekvireringsstatus,
Identtype identtype,
Boolean syntetisk,
LocalDate foedtEtter,
LocalDate foedtFoer);

@Query
int countAllByRekvireringsstatusAndIdenttypeAndSyntetiskAndKjoennAndFoedselsdatoBetween(
Rekvireringsstatus rekvireringsstatus,
Identtype identtype,
Boolean syntetisk,
Kjoenn kjoenn,
LocalDate foedtEtter,
LocalDate foedtFoer);

@Query
Page<Ident> findAllByRekvireringsstatusAndIdenttypeAndSyntetiskAndFoedselsdatoBetween(
Rekvireringsstatus rekvireringsstatus,
Identtype identtype,
Boolean syntetisk,
LocalDate foedtEtter,
LocalDate foedtFoer,
Pageable pageable);

@Query
Page<Ident> findAllByRekvireringsstatusAndIdenttypeAndSyntetiskAndKjoennAndFoedselsdatoBetween(
Rekvireringsstatus rekvireringsstatus,
Identtype identtype,
Boolean syntetisk,
Kjoenn kjoenn,
LocalDate foedtEtter,
LocalDate foedtFoer,
Pageable pageable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,59 +8,75 @@
import no.nav.testnav.identpool.repository.IdentRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

import static java.util.Objects.nonNull;
import static org.apache.commons.lang3.BooleanUtils.isTrue;

@Service
@RequiredArgsConstructor
public class DatabaseService {

private static final Random RANDOM = new SecureRandom();

private final IdentRepository identRepository;
private final MapperFacade mapperFacade;

public Set<Ident> hentLedigeIdenterFraDatabase(HentIdenterRequest request) {
Set<Ident> identEntities = new HashSet<>();

HentIdenterRequest availableIdentsRequest = mapperFacade.map(request, HentIdenterRequest.class);

var firstPage = findPage(availableIdentsRequest, Rekvireringsstatus.LEDIG, 0);
var pageCache = new HashMap<Integer, Page<Ident>>();
pageCache.put(0, firstPage);

int totalPages = firstPage.getTotalPages();
if (totalPages > 0) {
List<String> usedIdents = new ArrayList<>();
SecureRandom rand = new SecureRandom();
for (var i = 0; i < request.getAntall(); i++) {
var randomPageNumber = rand.nextInt(totalPages);
pageCache.computeIfAbsent(randomPageNumber, k ->
findPage(availableIdentsRequest, Rekvireringsstatus.LEDIG, randomPageNumber));

List<Ident> content = pageCache.get(randomPageNumber).getContent();
for (Ident ident : content) {
if (!usedIdents.contains(ident.getPersonidentifikator())) {
usedIdents.add(ident.getPersonidentifikator());
identEntities.add(ident);
break;
}
}
}

var availableIdentsRequest = mapperFacade.map(request, HentIdenterRequest.class);

var antall = getAntall(availableIdentsRequest);

if (antall == 0) {
return new HashSet<>();
}
return identEntities;

if (antall > request.getAntall()) {
var resultat = getPage(request, PageRequest.of(RANDOM.nextInt(antall/request.getAntall()), request.getAntall()));
return new HashSet<>(resultat.getContent());
}

return new HashSet<>(
getPage(request, PageRequest.of(0, request.getAntall()))
.getContent());
}

private Page<Ident> findPage(HentIdenterRequest request, Rekvireringsstatus rekvireringsstatus, int page) {
private int getAntall(HentIdenterRequest request) {

return nonNull(request.getKjoenn()) ?

identRepository.countAllByRekvireringsstatusAndIdenttypeAndSyntetiskAndKjoennAndFoedselsdatoBetween(
Rekvireringsstatus.LEDIG, request.getIdenttype(),
isTrue(request.getSyntetisk()), request.getKjoenn(),
request.getFoedtEtter(), request.getFoedtFoer()) :

identRepository.countAllByRekvireringsstatusAndIdenttypeAndSyntetiskAndFoedselsdatoBetween(
Rekvireringsstatus.LEDIG, request.getIdenttype(),
isTrue(request.getSyntetisk()),
request.getFoedtEtter(), request.getFoedtFoer());
}

private Page<Ident> getPage(HentIdenterRequest request, Pageable page) {

return nonNull(request.getKjoenn()) ?

identRepository.findAllByRekvireringsstatusAndIdenttypeAndSyntetiskAndKjoennAndFoedselsdatoBetween(
Rekvireringsstatus.LEDIG, request.getIdenttype(),
isTrue(request.getSyntetisk()), request.getKjoenn(),
request.getFoedtEtter(), request.getFoedtFoer(), page
) :

return identRepository.findAll(
rekvireringsstatus, request.getIdenttype(), request.getKjoenn(), request.getFoedtFoer(),
request.getFoedtEtter(), isTrue(request.getSyntetisk()), PageRequest.of(page, request.getAntall()));
identRepository.findAllByRekvireringsstatusAndIdenttypeAndSyntetiskAndFoedselsdatoBetween(
Rekvireringsstatus.LEDIG, request.getIdenttype(),
isTrue(request.getSyntetisk()),
request.getFoedtEtter(), request.getFoedtFoer(), page
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
Expand All @@ -31,7 +32,7 @@
public class IdentGeneratorService {

private static final int SYNTETISK = 4;
private static final SecureRandom random = new SecureRandom();
private static final Random random = new SecureRandom();

private static String addSyntetiskIdentifier(String format) {
return String.format("%s%1d%s", format.substring(0, 2), Integer.parseInt(format.substring(2, 3)) + SYNTETISK, format.substring(3));
Expand Down Expand Up @@ -67,45 +68,47 @@ public Set<String> genererIdenter(HentIdenterRequest request, Set<String> idente
request.setFoedtFoer(request.getFoedtEtter().plusDays(1));
}

var identer = identerIIdentPool;
var antall = request.getAntall() + identer.size();
var antall = request.getAntall() + identerIIdentPool.size();
var iteratorRange = (request.getKjoenn() == null) ? 1 : 2;
var numberOfDates = toIntExact(ChronoUnit.DAYS.between(request.getFoedtEtter(), request.getFoedtFoer()));

Function<LocalDate, String> numberFormat =
numberFormatter.getOrDefault(request.getIdenttype(), IdentGeneratorUtil::randomFormat);

while (identer.size() < antall) {
while (identerIIdentPool.size() < antall) {
var birthdate = request.getFoedtEtter().plusDays(random.nextInt(numberOfDates));
var format = numberFormat.apply(birthdate);
if (isTrue(request.getSyntetisk())) {
format = addSyntetiskIdentifier(format);
}

var yearRange = getYearRange(birthdate);
var originalSize = identer.size();
var originalSize = identerIIdentPool.size();
var genderNumber = getGenderNumber(yearRange, request.getKjoenn());
var startIndex = getStartIndex(yearRange.get(0), request.getKjoenn());

for (int i = startIndex; identerIIdentPool.size() == originalSize && i < genderNumber; i += iteratorRange) {
String fnr = generateFnr(String.format(format, i));
if (fnr != null) {
identer.add(fnr);
identerIIdentPool.add(fnr);
}
}

for (int i = genderNumber; identer.size() == originalSize && i < yearRange.get(1); i += iteratorRange) {
String fnr = generateFnr(String.format(format, i));
for (int i = genderNumber; identerIIdentPool.size() == originalSize && i < yearRange.get(1); i += iteratorRange) {
var fnr = generateFnr(String.format(format, i));
if (fnr != null) {
identer.add(fnr);
identerIIdentPool.add(fnr);
}
}

if (identerIIdentPool.size() == originalSize) {
throw new IllegalArgumentException("Kan ikke finne ønsket antall fødselsnummer med angitte kriterier");
break;
}
}
return identer;
if (identerIIdentPool.isEmpty()) {
throw new IllegalArgumentException("Finner ingen fødselsnummer med angitte kriterier");
}
return identerIIdentPool;
}

private void validateDates(LocalDate foedtEtter, LocalDate foedtFoer) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,14 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import no.nav.testnav.identpool.domain.Ident;
import no.nav.testnav.identpool.domain.Identtype;
import no.nav.testnav.identpool.domain.Rekvireringsstatus;
import no.nav.testnav.identpool.dto.TpsStatusDTO;
import no.nav.testnav.identpool.exception.ForFaaLedigeIdenterException;
import no.nav.testnav.identpool.providers.v1.support.HentIdenterRequest;
import no.nav.testnav.identpool.repository.IdentRepository;
import org.springframework.stereotype.Service;

import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static java.lang.String.format;
import static java.time.format.DateTimeFormatter.ISO_DATE;
Expand Down Expand Up @@ -64,19 +60,22 @@ private static void logRequest(HentIdenterRequest request) {

public synchronized List<String> allocateIdenter(HentIdenterRequest request) {

Set<Ident> identEntities = databaseService.hentLedigeIdenterFraDatabase(request);
var identEntities = databaseService.hentLedigeIdenterFraDatabase(request);
int missingIdentCount = request.getAntall() - identEntities.size();

if (missingIdentCount > 0) {

var tpsStatusDTOS = identerAvailService.generateAndCheckIdenter(request, ATTEMPT_OBTAIN);
var statusDTOS = identerAvailService.generateAndCheckIdenter(request,
isTrue(request.getSyntetisk()) ? ATTEMPT_OBTAIN * 12 : ATTEMPT_OBTAIN);

List<Ident> identerFraTps = tpsStatusDTOS.stream()
log.info("Generert {} identer ved mining", statusDTOS.size());

var identerFraMining = statusDTOS.stream()
.map(this::buildIdent)
.toList();
identRepository.saveAll(identerFraTps);
identRepository.saveAll(identerFraMining);

Iterator<Ident> ledigeIdents = identerFraTps.stream()
var ledigeIdents = identerFraMining.stream()
.filter(Ident::isLedig)
.toList().iterator();

Expand Down
34 changes: 12 additions & 22 deletions apps/testnav-ident-pool/src/main/resources/application-local.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,17 @@
spring:
cloud:
vault:
connection-timeout: 15000
fail-fast: true
host: vault.adeo.no
port: 443
read-timeout: 30000
h2:
console:
enabled: true
path: /h2
datasource:
hikari:
maximum-pool-size: 3
minimum-idle: 1
url: jdbc:postgresql://localhost:5432/ident-pool-test
username: postgres
flyway:
baseline-on-migrate: true
enabled: true # Disabled by default as you should probably think twice before running Flyway-migrations
locations: classpath:/db/migration
jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
default-schema: public
showSql: true
url: jdbc:h2:mem:testdb
username: sa
password:
driverClassName: org.h2.Driver
sql:
init:
mode: always
data-locations: classpath:/db/dev/h2-default-config.sql

consumers:
tps:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@

spring:
oauth2:
tokenx:
issuer-uri: ${TOKEN_X_ISSUER}
jwk-set-uri: ${TOKEN_X_JWKS_URI}
accepted-audience: ${TOKEN_X_CLIENT_ID}
datasource:
url: jdbc:postgresql://${NAIS_DATABASE_TESTNAV_IDENTPOOL_TESTNAV_IDENTPOOL_HOST}:${NAIS_DATABASE_TESTNAV_IDENTPOOL_TESTNAV_IDENTPOOL_PORT}/${NAIS_DATABASE_TESTNAV_IDENTPOOL_TESTNAV_IDENTPOOL_DATABASE}?user=${NAIS_DATABASE_TESTNAV_IDENTPOOL_TESTNAV_IDENTPOOL_USERNAME}&password=${NAIS_DATABASE_TESTNAV_IDENTPOOL_TESTNAV_IDENTPOOL_PASSWORD}
driverClassName: org.postgresql.Driver
hikari:
maximum-pool-size: 3
minimum-idle: 1
minimum-idle: 1
Loading