Skip to content

Commit

Permalink
implemented statistics services and refactor helper services
Browse files Browse the repository at this point in the history
  • Loading branch information
guiguilechat committed Jul 31, 2024
1 parent 3ad0e83 commit f1b7106
Show file tree
Hide file tree
Showing 18 changed files with 556 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import fr.guiguilechat.jcelechat.jcesi.interfaces.Requested;
import fr.guiguilechat.jcelechat.libs.spring.affiliations.corporation.CorporationInfo;
import fr.guiguilechat.jcelechat.libs.spring.affiliations.corporation.CorporationInfoService;
import fr.guiguilechat.jcelechat.libs.spring.update.fetched.batch.ABatchResourceFetcher;
import fr.guiguilechat.jcelechat.libs.spring.update.fetched.remote.batch.AResourceBatchFetcher;
import fr.guiguilechat.jcelechat.model.jcesi.compiler.compiled.responses.R_get_universe_factions;
import lombok.RequiredArgsConstructor;

Expand All @@ -21,7 +21,7 @@
@ConfigurationProperties(prefix = "esi.affiliations.faction")
@Order(1)
public class FactionInfoService
extends ABatchResourceFetcher<FactionInfo, Integer, R_get_universe_factions, FactionInfoRepository> {
extends AResourceBatchFetcher<FactionInfo, Integer, R_get_universe_factions, FactionInfoRepository> {

@Lazy
private final CorporationInfoService corporationInfoService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ protected void updateResponseOk(Map<MarketGroup, R_get_markets_groups_market_gro
mg.setParent(idToMarketGroup.get(data.parent_group_id));
for (int tid : data.types) {
Type type = idtoType.get(tid);
if (type.getMarketGroup() != null && type.getMarketGroup().getId() != mg.getId()) {
if (type.getMarketGroup() != null && !mg.getId().equals(type.getMarketGroup().getId())) {
log.warn("type {} marketgroup changed from {}({}) to {}({})", type.name(), type.getMarketGroup().name(),
type.getMarketGroup().getId(), mg.name(), mg.getId());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package fr.guiguilechat.jcelechat.libs.spring.update.batch;

import java.time.Instant;
import java.util.List;

import fr.guiguilechat.jcelechat.libs.spring.update.batch.BatchFetch.BatchItem;
import jakarta.persistence.Column;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.OneToMany;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
* a batch fetch
*/
@MappedSuperclass
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class BatchFetch<Item extends BatchItem<?, ?>> {

/** an, item that is created from a batch fetch */
@MappedSuperclass
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public static abstract class BatchItem<Fetch extends BatchFetch<?>, Fetched> {

@ManyToOne
private Fetch fetch;

public abstract BatchItem<Fetch, Fetched> update(Fetched fetched);

}

@OneToMany(mappedBy = "fetch")
private List<Item> items;

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;

@Column(columnDefinition = "TEXT")
private String errorMessage = null;

private String etag;

private Instant expires;

private Instant lastModified;

private int nbItems = 0;

private int responseCode = 0;

public static enum STATUS {
SUCCESS, ERROR, CACHED, CREATED
}

@Enumerated(EnumType.STRING)
private STATUS status = STATUS.CREATED;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package fr.guiguilechat.jcelechat.libs.spring.update.batch;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.NoRepositoryBean;

import fr.guiguilechat.jcelechat.libs.spring.update.batch.BatchFetch.STATUS;

@NoRepositoryBean
public interface BatchFetchRepository<Fetch extends BatchFetch<?>> extends JpaRepository<Fetch, Long> {

public Fetch findTop1ByStatusOrderByLastModifiedDesc(STATUS status);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package fr.guiguilechat.jcelechat.libs.spring.update.batch;

import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.JpaRepository;

import fr.guiguilechat.jcelechat.jcesi.ConnectedImpl;
import fr.guiguilechat.jcelechat.jcesi.interfaces.Requested;
import fr.guiguilechat.jcelechat.libs.spring.update.batch.BatchFetch.BatchItem;
import fr.guiguilechat.jcelechat.libs.spring.update.batch.BatchFetch.STATUS;
import fr.guiguilechat.jcelechat.libs.spring.update.manager.IEntityUpdater;
import fr.guiguilechat.jcelechat.libs.spring.update.resolve.status.ESIStatusService;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public abstract class BatchFetchService<
Fetch extends BatchFetch<Item>,
FetchRepo extends BatchFetchRepository<Fetch>,
Item extends BatchItem<Fetch, ?>,
ItemRepo extends JpaRepository<Item, ?>, Structure
>
implements IEntityUpdater {


@Autowired // can't use constructor injection for generic service
@Accessors(fluent = true)
@Getter(value = AccessLevel.PROTECTED)
private ESIStatusService esiStatusService;

@Autowired // can't use constructor injection for generic service
@Accessors(fluent = true)
@Getter(value = AccessLevel.PROTECTED)
private FetchRepo fetchRepository;

@Autowired // can't use constructor injection for generic service
@Accessors(fluent = true)
@Getter(value = AccessLevel.PROTECTED)
private ItemRepo itemRepository;


@Getter
private final UpdateConfig update = new UpdateConfig();

/**
* following fetch date, if any. If not set, default method to use the
* {@link UpdateConfig}. It is reset during the {@link #preUpdate()}
*/
@Setter(value = AccessLevel.PROTECTED)
private Instant nextUpdate = null;

@Override
public Instant nextUpdate(boolean remain, Instant now) {
if (nextUpdate != null) {
return nextUpdate;
}
return IEntityUpdater.super.nextUpdate(remain, now);
}

protected abstract Requested<Structure> fetchData(Map<String, String> properties);

protected abstract Fetch createFetch();

public Fetch lastSuccess() {
return fetchRepository().findTop1ByStatusOrderByLastModifiedDesc(STATUS.SUCCESS);
}


@Override
public boolean fetch() {
Fetch lastSuccess = lastSuccess();
if (lastSuccess != null && Instant.now().isBefore(lastSuccess.getExpires())) {
return false;
}
int errorsRemain = esiStatusService().availErrors();
if (errorsRemain < getUpdate().getErrorsMin()) {
setNextUpdate(esiStatusService().getErrorReset());
return true;
}

log.debug("{} fetching data with last etag {}", fetcherName(), lastSuccess == null ? null : lastSuccess.getEtag());
Map<String, String> properties = new HashMap<>();
if (lastSuccess != null) {
properties.put(ConnectedImpl.IFNONEMATCH, lastSuccess.getEtag());
}
Fetch fetchResult = createFetch();
fetchResult = fetchRepository().save(fetchResult);
Requested<Structure> response = fetchData(properties);
if (response == null) {
updateNullResponse(fetchResult);
} else {
int responseCode = response.getResponseCode();
fetchResult.setResponseCode(responseCode);
switch (responseCode) {
case 200:
updateOk(fetchResult, response);
break;
case 204: // no content => null
updateNullBody(fetchResult, response);
break;
case 304:
updateNoChange(fetchResult, response);
break;
default:
switch (responseCode / 100) {
case 4:
updateRequestError(fetchResult, response);
break;
case 5:
updateServerError(fetchResult, response);
break;
default:
log.error("{} while fetching, received response code {} and error {}",
fetcherName(), responseCode, response.getError());
throw new UnsupportedOperationException("case " + responseCode + " not handled for url" + response.getURL());
}
}
}
fetchRepository().saveAndFlush(fetchResult);
return false;
}

protected void updateNullResponse(Fetch fetchResult) {
fetchResult.setErrorMessage("null response");
fetchResult.setResponseCode(0);
fetchResult.setStatus(STATUS.ERROR);
}

protected void updateOk(Fetch fetchResult, Requested<Structure> response) {
updateMetaOk(fetchResult, response);
List<Item> converted = convert(fetchResult, response.getOK());
converted.forEach(it -> it.setFetch(fetchResult));
itemRepository().saveAllAndFlush(converted);
fetchResult.setNbItems(converted.size());
log.debug(" {} saving {} new items", fetcherName(), converted.size());
}

protected void updateMetaOk(Fetch fetchResult, Requested<Structure> response) {
fetchResult.setEtag(response.getETag());
fetchResult.setExpires(response.getExpiresInstant());
fetchResult.setLastModified(response.getLastModifiedInstant());
fetchResult.setStatus(STATUS.SUCCESS);
}

protected abstract List<Item> convert(Fetch fetchResult, Structure response);

private void updateNullBody(Fetch fetchResult, Requested<Structure> response) {
updateMetaOk(fetchResult, response);
}

private void updateNoChange(Fetch fetchResult, Requested<Structure> response) {
fetchResult.setStatus(STATUS.CACHED);
}

private void updateRequestError(Fetch fetchResult, Requested<Structure> response) {
fetchResult.setErrorMessage(response.getError());
fetchResult.setStatus(STATUS.ERROR);
}

private void updateServerError(Fetch fetchResult, Requested<Structure> response) {
fetchResult.setErrorMessage(response.getError());
fetchResult.setStatus(STATUS.ERROR);
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package fr.guiguilechat.jcelechat.libs.spring.update.fetched.batch;
package fr.guiguilechat.jcelechat.libs.spring.update.fetched.remote.batch;

import java.time.Instant;
import java.util.HashMap;
Expand All @@ -24,7 +24,7 @@
* @param <Repository>
*/
@Slf4j
public abstract class ABatchResourceFetcher<
public abstract class AResourceBatchFetcher<
Entity extends AFetchedResource<Id>,
Id extends Number,
Fetched,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public static class UpdateConfig {
*
* <pre>{@code
* @Getter
* private UpdateConfig update;
* private final UpdateConfig update = new UpdateConfig();
* }</pre>
*/
public UpdateConfig getUpdate();
Expand All @@ -69,7 +69,8 @@ public static class UpdateConfig {
public boolean fetch();

/**
* deduce the next Instant to fetch based on previous result of {@link #fetch()}
* deduce the next Instant to fetch based on previous execution of
* {@link #fetch()}
*
* @param remain result of last fetch
* @param startTime time when the fetch started
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ protected boolean skipService(IEntityUpdater updater) {

@PostConstruct
protected void debugConfig() {
log.debug("configuration of {} registered updaters : ", fetchedServices.orElse(List.of()).size());
log.debug("configuration of {} registered updaters, with manager skip={} defaultSkip={} updatedDelay={}",
fetchedServices.orElse(List.of()).size(), skip, defaultSkip, updatedDelay);
for (IEntityUpdater l : fetchedServices.orElse(List.of())) {
log.debug("{} ({}): {}", l.fetcherName(), skipService(l) ? "skiped" : "active", l.getUpdate());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public int availErrors() {
if (!esiAccess.vip) {
return esiAccessReq.getRemainingErrors();
}
errorReset = Instant.now().plusSeconds(60);
log.info(" ESI is in VIP mode, skipping");
return 0;
}
Expand All @@ -47,6 +48,7 @@ public int availErrors() {
log.debug("esi not avail (503), prevent fetch until {}", errorReset);
break;
default:
errorReset = Instant.now().plusSeconds(10);
log.info(" could not get ESI status, skipping for {}", esiAccessReq.getResponseCode());
}
return 0;
Expand Down
Loading

0 comments on commit f1b7106

Please sign in to comment.