Ho un servizio che invia una serie di query al server in una singola chiamata e agisce sulle decisioni prese dal server. Per prima cosa introdurrò ciò che ho già e le domande seguiranno.
public class Item {
int id;
int version;
String description;
}
public class RequestObject {
Item key;
List<Item> candidates;
}
public class ResultObject {
Item key,
Item candidate,
Boolean isActionable;
}
public class Client {
List<ResultObject> post(RequestObject request) {
// Makes a rest call to the server and returns the results
}
}
/** Server side **/
public class ServiceServlet {
private ActionDecider actionDecider;
List<ResultObject> processRequest(RequestObject request) {
List<ResultObject> results = new ArrayList<>();
for(Item c : request.candidates) {
// decider reads values of key and c from database, returns the results
results.add(actionDecider.decide(request.key, c));
}
return results;
}
}
/** Client side **/
public class ClientService {
private ResultUpdater updater;
public void processMessage(Item key, List<Item> candidates) {
RequestObject request = new RequestObject(key, candidates);
for(ResultObject result : client.post(request)) {
if(result.isActionable) {
// update values of key and c in database, if key provided has a different version that in the database, throws exception
updater.update(result.key, result.candidate);
}
}
}
}
Nota:
- Il numero di candidati in una richiesta è di circa 100. E in media recuperiamo circa 5 coppie di azioni.
-
actionDecider.decide
è un'operazione costosa, quindi vogliamo evitare di eseguirla inutilmente più di una volta per la stessa coppia di articoli.
Problema: quando ci sono più risultati utilizzabili e il programma di aggiornamento aggiorna la chiave la prima volta, il valore nel database cambia e quindi il resto dei risultati è obsoleto. Il resto dei messaggi deve essere rivalutato chiamando il servizio lato server prima che possano essere aggiornati.
Una soluzione semplice sarebbe inviare ciascun candidato alla volta come richiesta al server, aggiornare il risultato se possibile e continuare con il candidato successivo. Tuttavia, questo rende K il numero di richieste invece di fare una singola richiesta. Ciò significa che subiremo quasi K volte più la latenza della rete.
public class ClientService {
private ResultUpdater updater;
public void processMessage(Item key, List<Item> candidates) {
for(Item candidate : candidates) {
RequestObject request = new RequestObject(key, candidate);
ResultObject result = client.post(request).get(0);
if(result.isActionable) {
// update values of key and c in database, if key provided has a different version that in the database, throws exception
updater.update(result.key, result.candidate);
}
}
}
}
La soluzione alternativa che sto pensando è la seguente. Dal momento che anche noi possediamo il servizio server, possiamo modificare l'oggetto richiesta in modo che contenga un flag che chiede al server di restituire il primo risultato utilizzabile saltando i candidati rimanenti nella richiesta. Allo stesso modo, possiamo creare un altro client che tenga traccia dei candidati che ha già inviato al server per il confronto e quali sono i restanti.
public class OptimizedClient {
public final RequestObject request;
private List<Item> remainingCandidates;
public OptimizedClient(RequestObject request) {
this.request = request;
this.remainingCandidates = request.candidates;
}
public ResultObject getNextActionable() {
// makes call to the server with the remaining candidates and check if it got a first set of matched results
RequestObject remainingRequest = new RequestObject(request.key, request.remaningCandidates);
// make rest call
List<ResultObject> results = restClient.post(remainingRequest);
if(results != null) {
int from = remainingCandidates.indexOf(results.get(0)) + 1;
int to = remainingCandidates.size();
remainingRequest = remainingCandidates.subList(from, to);
return results.get(0);
}
return null;
}
}
Esiste uno schema standard per questo tipo di problema? In caso contrario, posso apportare miglioramenti alla mia soluzione ottimizzata proposta?