Co kilkadziesiąt sekund 1 wątek woła cachedSimpleService.update()
i równocześnie cały czas miliardy klientów uderza przez http do controlera, który woła cachedSimpleService.getXmlData(${page})
. Co kilka dni aplikacja się wykrzacza, wchodzi w zły stan i trzeba ją zrestartować. Wylatuje ArrayIndexOutOfBoundsException na każdy cachedSimpleService.getXmlData(${page})
.
Weak, "eventual consistancy" jest wystarczające, klienty nie muszą widzieć tych samych danych od razu ani aktualnych, ważne by w końcu za którymś zapytaniem się odświeżyły dlatego nic tu nie gmerałem z blokami synchronized itp.
Widzicie jakiś błąd w tym kodzie?
class CachedSimpleService implements MyService {
private final SimpleService myService;
private Map<Integer, String> xmlDataPaged;
public CachedSimpleService(MyService myService) {
this.myService = myService;
this.dataPaged = new ConcurrentHashMap<>();
}
@Override
public String getXmlData(int page) {
String xml = xmlDataPaged.get(page);
if (xml == null) {
xml = myService.getXmlData(page);
xmlDataPaged.put(page, xml);
}
return xml;
}
@Override
public void update() {
this.myService.update();
invalidateCache();
}
private void invalidateCache() {
this.xmlDataPaged = new ConcurrentHashMap<>();
}
}
class SimpleService implements MyService {
private final DataHolder dataHolder;
private final PageableMapper pageableMapper;
private final XmlParser xmlParser;
@Override
public String getXmlData(int page) {
var data = dataHolder.getData();
var singlePageFromData = pageableMapper.from(data, page);
return xmlParser.parse(singlePageFromData); // ===> tu leci java.lang.ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 47
}
@Override
public void update() {
this.dataHolder.update();
}
}
class DataHolder {
private final DataRepo dataRepo;
private Data data;
public Data getData() {
if (data == null) {
throw new DataNotInitializedException("Data not prepared yet. Please try again later");
}
return data;
}
public void update() {
this.data = getData();
}
private Data getData() {
// ...
}
}
zrobiłbym tak, ale nie wiem czy to jest przyczyną tych błędów:
class CachedSimpleService implements MyService {
private final SimpleService myService;
private volatile Map<Integer, String> xmlDataPaged;
public CachedSimpleService(MyService myService) {
this.myService = myService;
this.dataPaged = new ConcurrentHashMap<>();
}
@Override
public String getXmlData(int page) {
String xml = xmlDataPaged.get(page);
if (xml == null) {z
synchronized(xmlDataPaged) { // ===> changed
if(xml == null { // ===> changed
xml = myService.getXmlData(page);
xmlDataPaged.put(page, xml);
}
}
}
return xml;
}
@Override
public void update() {
this.myService.update();
invalidateCache();
}
private void invalidateCache() {
this.xmlDataPaged = new ConcurrentHashMap<>();
}
}
class DataHolder {
private final DataRepo dataRepo;
private volatile Data data;
public synchronized Data getData() { // ===> changed
if (data == null) {
throw new DataNotInitializedException("Data not prepared yet. Please try again later");
}
return data;
}
public synchronized void update() { // ===> changed
this.data = getData();
}
private Data getData() {
// ...
}
}