/* * Copyright 2012 LinkedIn, Inc * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.linkedin.parseq.example.composite; import com.linkedin.parseq.BaseTask; import com.linkedin.parseq.Context; import com.linkedin.parseq.Engine; import com.linkedin.parseq.Task; import com.linkedin.parseq.example.common.AbstractExample; import com.linkedin.parseq.example.common.MockService; import com.linkedin.parseq.example.common.SimpleMockRequest; import com.linkedin.parseq.promise.Promise; import com.linkedin.parseq.promise.Promises; import com.linkedin.parseq.promise.SettablePromise; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import static com.linkedin.parseq.Tasks.action; import static com.linkedin.parseq.Tasks.seq; import static com.linkedin.parseq.example.common.ExampleUtil.callService; import static com.linkedin.parseq.example.common.ExampleUtil.printTracingResults; /** * @author Chris Pettitt (cpettitt@linkedin.com) */ public class TimeBoundSearchExample extends AbstractExample { // How long it takes to get a response for each request private static final long[] REQUEST_LATENCIES = new long[] {175, 67, 30, 20, 177, 350}; // How long the engine will wait for index number of responses private static final long[] WAIT_TIMES = new long[] {400, 300, 200, 100, 0}; public static void main(String[] args) throws Exception { new TimeBoundSearchExample().runExample(); } @Override protected void doRunExample(final Engine engine) throws Exception { final MockService<Integer> service = getService(); final SearchTask example = new SearchTask(service); System.out.printf("This com.linkedin.asm.example will issue %d parallel requests\n", REQUEST_LATENCIES.length); System.out.println(); for (int i = 0; i < REQUEST_LATENCIES.length; i++) { System.out.printf("Request %d will take %3dms to complete\n", i, REQUEST_LATENCIES[i]); } System.out.println(); System.out.println("Latency rules:"); System.out.println("--------------"); for (int i = 0; i < WAIT_TIMES.length; i++) { System.out.printf("Finish if received %d responses after %3dms\n", i, WAIT_TIMES[i]); } System.out.println(); System.out.println("Execution:"); System.out.println("----------"); final long startMillis = System.currentTimeMillis(); engine.run(example); example.await(); final long endMillis = System.currentTimeMillis(); System.out.println("Responses: " + example.get()); System.out.println("Execution time: " + (endMillis - startMillis) + "ms"); printTracingResults(example); } private static class SearchTask extends BaseTask<List<Integer>> { private final MockService<Integer> _service; private final List<Integer> _responses = new ArrayList<Integer>(); private final SettablePromise<List<Integer>> _result = Promises.settable(); private long _startMillis; public SearchTask(final MockService<Integer> service) { super("search"); _service = service; } @Override public Promise<List<Integer>> run(final Context ctx) { // Save the start time so we can determine when to finish _startMillis = System.currentTimeMillis(); // Set up timeouts for responses long lastWaitTime = Integer.MAX_VALUE; for (final long waitTime : WAIT_TIMES) { if (waitTime < lastWaitTime && waitTime > 0) { ctx.createTimer(waitTime, TimeUnit.MILLISECONDS, checkDone()); lastWaitTime = waitTime; } } // Issue requests for (int i = 0; i < REQUEST_LATENCIES.length; i++) { final long requestLatency = REQUEST_LATENCIES[i]; final Task<Integer> callSvc = callService("subSearch[" + i + "]", _service, new SimpleMockRequest<Integer>(requestLatency, i)); ctx.run(seq(callSvc, addResponse(callSvc), checkDone())); } return _result; } private Task<?> checkDone() { return action("checkDone", new Runnable() { @Override public void run() { final int index = Math.min(WAIT_TIMES.length - 1, _responses.size()); if (WAIT_TIMES[index] + _startMillis <= System.currentTimeMillis()) { _result.done(_responses); } } }); } private Task<?> addResponse(final Promise<Integer> response) { return action("addResponse", new Runnable() { @Override public void run() { _responses.add(response.get()); } }); } } }