0

I have a service which sends a set of queries to the server in a single call and acts on the decisions from the server. I will first introduce what I already have and the questions will follow.

   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);
            }
        }
    }
}

Note:

  • The number of candidates in a request is around 100. And on average we get around 5 actionable pairs back.
  • actionDecider.decide is an expensive operation so we want to avoid running it unnecessarily more than once for the same pair of items.

Problem: When there are multiple actionable results and updater updates the key the first time, the value in the database changes and hence the rest of the results are stale. The rest of the messages have to be re-evaluated by calling the server side service before they can be updated.

One simple solution would be to send each one candidate at the time as a request to the server, update the result if actionable and continue with the next candidate. However, this makes K number of requests instead of making a single request. This means we will be incurring almost K times more the network latency.

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);
            }
        }
    }
}

Alternative solution I am thinking is the following. Since we own the server service as well, we can modify the request object to contain a flag asking the server to return the first actionable result skipping the remaining candidates in the request. Similarly, we can come up with a different client which keeps track of the candidates that it has already sent to the server for comparison and what are the remaining ones.

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;
    }
}

Is there a standard pattern for this kind of problem? If not, can I make improvements to my proposed optimized solution?

  • 2
    This question would be easier to understand if you described the components and how they interact. I feel all that code is unnecessary specific (and makes getting to the question hard). – Eiko Oct 9 '16 at 9:30
  • Why does the key change? Maybe it shouldn't. I mean, maybe you should be using some other data that doesn't change when you call update. That way you could update things freely, without having to refetch them. – Martin Jungblut Schreiner Oct 11 '16 at 8:32
  • The update method updates the data on the item. It changes the description field and increases the version to signify data has changed. – learnerer Oct 12 '16 at 11:10
1

Problem: When there are multiple actionable results and updater updates the key the first time, the value in the database changes and hence the rest of the results are stale. The rest of the messages have to be re-evaluated by calling the server side service before they can be updated.

You are right, this seems to be a problem. Your post is very generic so it is hard to reason for a better solution but why is updating a single resource in your database is effecting the other resources in the database in such a disruptive manner? If possible I would re-evaluate the design decision that lead to this problem.

  • It is not disrupting other resources. When the key is updated due to the first action, it's version changes signifying it has changed. In rest of the actionable pairs, when we try to update key using the old version, it throws an exception. – learnerer Oct 9 '16 at 17:40
  • @learnerer Then refetch it after update ? Of update manually the version number if you're really sure of what you're doing. It's an optimistic locking problem. – Walfrat Feb 6 '17 at 12:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.