Git Product home page Git Product logo

parseq's Introduction

ParSeq

License Build Status

ParSeq is a framework that makes it easier to write asynchronous code in Java.

Some of the key benefits of ParSeq include:

Our Wiki includes an introductory example, a User's Guide, javadoc, and more.

See CHANGELOG for list of changes.

Introductory Example

In this example we show how to fetch several pages in parallel and how to combine them once they've all been retrieved.

You can find source code here: IntroductoryExample.

First we can retrieve a single page using an asynchronous HTTP client as follows:

    final Task<Response> google = HttpClient.get("http://www.google.com").task();
    engine.run(google);
    google.await();
    System.out.println("Google Page: " + google.get().getResponseBody());

This will print:

Google Page: <!doctype html><html>...

In this code snippet we don't really get any benefit from ParSeq. Essentially we create a task that can be run asynchronously, but then we block for completion using google.await(). In this case, the code is more complicated than issuing a simple synchronous call. We can improve this by making it asynchronous:

    final Task<String> google = HttpClient.get("http://www.google.com").task()
        .map(Response::getResponseBody)
        .andThen(body -> System.out.println("Google Page: " + body));

    engine.run(google);

We used map method to transform Response into the String and andThen method to print out result. Now, let's expand the example so that we can fetch a few more pages in parallel. First, let's create a helper method that creates a task responsible for fetching page body given a URL.

    private Task<String> fetchBody(String url) {
      return HttpClient.get(url).task().map(Response::getResponseBody);
    }

Next, we will compose tasks to run in parallel using Task.par.

    final Task<String> google = fetchBody("http://www.google.com");
    final Task<String> yahoo = fetchBody("http://www.yahoo.com");
    final Task<String> bing = fetchBody("http://www.bing.com");

    final Task<String> plan = Task.par(google, yahoo, bing)
        .map((g, y, b) -> "Google Page: " + g +" \n" +
                          "Yahoo Page: " + y + "\n" +
                          "Bing Page: " + b + "\n")
        .andThen(System.out::println);

    engine.run(plan);

This example is fully asynchronous. The home pages for Google, Yahoo, and Bing are all fetched in parallel while the original thread has returned to the calling code. We used Tasks.par to tell the engine to parallelize these HTTP requests. Once all of the responses have been retrieved they are transformed into a String that is finally printed out.

We can do various transforms on the data we retrieved. Here's a very simple transform that sums the length of the 3 pages that were fetched:

    final Task<Integer> sumLengths =
        Task.par(google.map(String::length),
                 yahoo.map(String::length),
                 bing.map(String::length))
             .map("sum", (g, y, b) -> g + y + b);

The sumLengths task can be given to an engine for execution and its result value will be set to the sum of the length of the 3 fetched pages.

Notice that we added descriptions to tasks. e.g. map("sum", (g, y, b) -> g + y + b). Using ParSeq's trace visualization tools we can visualize execution of the plan. Waterfall graph shows tasks execution in time (notice how all GET requests are executed in parallel):

sum-lengths-waterfall-example.png

Graphviz diagram best describes relationships between tasks:

sum-lengths-graphviz-example.png

For more in-depth description of ParSeq please visit User's Guide.

For many more examples, please see the parseq-examples contrib project in the source code.

Build

Build and test whole parseq code ./gradlew clean build

Build ParSeq subproject(modules) instead of the whole project: ./gradlew :<module_name>:build

Building on MacOS Catalina (>=10.15): Follow this guide to install the required Xcode Command Line Tools and add below environment variables

export LDFLAGS="-mmacosx-version-min=10.13"
export CXXFLAGS="-mmacosx-version-min=10.13"

parseq's People

Contributors

aman1309 avatar angxu avatar anmol-singh-jaggi avatar bolobobo avatar cbrentharris avatar chikit avatar cpettitt-linkedin avatar cx-super avatar darthsidpal avatar dependabot[bot] avatar dimapilis avatar dmitriy-yefremov avatar evanw555 avatar extesy avatar franklinyinanding avatar frecap avatar hiteshsharma avatar joejoe1989 avatar junchuanwang avatar logancarmody avatar mchen07 avatar mehtabsinghmann avatar michaelbitard avatar seliang avatar sleepytomcat avatar ssheng avatar ssodhanilinkedin avatar sweeboonlim avatar yahro avatar zackthehuman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

parseq's Issues

Improve accuracy of Task.withTimeout()

Currently the timeout action is executed as a timer task with the highest priority. It means that there is some delay between timer triggering and action being executed. In extreme cases the delay can be significant.
This task is to investigate different approach of executing timeouts that does not involve scheduling task and a plan's queue.

Use Maven or Gradle

Ant is barely used these days and it's harder to contribute when an obscure build system is used

Add Tasks.<T>seq(Iterable<? extends Task<?>>, Task<T> resultTask) method

Sometimes client needs to run a sequence of tasks, and then one final task that returns the result:

{
  Iterable<Task<Foo>> fooTasks = buildFooTasks(...); // may return empty collection
  Task<Bar> barTask = new BarTask();
  Task<Bar> foosThenBarSeq = Tasks.seq(Tasks.seq(fooTasks), barTask); // will fail if fooTasks is empty
  Task<Bar> foosThenBarSeq = Tasks.seq(fooTasks, barTask); // looks cleaner and less if..then..else blocks
  ...
}

Implement rate limiter

Sometimes, especially in offline/nearline scenarios it might be practical to use rate limiter.
When used with bounded max concurrent plans and Engine.blockingRun() it would apply back pressure based on specified rate limits.
Some of design considerations:

  • it should be non-blocking
  • cancellation of rate limited tasks that wait for a permit should release their reservations to allow other waiting tasks to start sooner

Statistical trace analytics

This task involves processing a sample of traces from production traffic and building performance model out of it. It should allow:

  • detecting blocking tasks
  • identifying bottlenecks and problems e.g. through critical path analysis
  • building interactive performance model that would allow what-if analysis and focus on optimization of specific type of work e.g. what do I need to improve in order to improve 99th percentile latency

RequestConfigProviderImpl.java is to chatty at INFO

Suggest:

diff --git a/contrib/parseq-restli-client/src/main/java/com/linkedin/restli/client/config/RequestConfigProviderImpl.java b/contrib/parseq-restli-client/src/mai
n/java/com/linkedin/restli/client/config/RequestConfigProviderImpl.java
index c0e0992..ddc83b9 100644
--- a/contrib/parseq-restli-client/src/main/java/com/linkedin/restli/client/config/RequestConfigProviderImpl.java
+++ b/contrib/parseq-restli-client/src/main/java/com/linkedin/restli/client/config/RequestConfigProviderImpl.java
@@ -60,14 +60,16 @@ class RequestConfigProviderImpl implements RequestConfigProvider {
       }
     }
     if (!failed) {
-      Collections.sort(elements);
-      StringBuilder sb = new StringBuilder();
-      sb.append("ParSeq RestLi Client Configuration for property " + property + " sorted by priority - first match gets applied:\n");
-      elements.forEach(el -> sb.append(el.getKey())
-                               .append(" = ")
-                               .append(el.getValue())
-                               .append("\n"));
-      LOGGER.info(sb.toString());
+      if (LOGGER.isTraceEnabled()) {
+        Collections.sort(elements);
+        StringBuilder sb = new StringBuilder();
+        sb.append("ParSeq RestLi Client Configuration for property " + property + " sorted by priority - first match gets applied:\n");
+        elements.forEach(el -> sb.append(el.getKey())
+                                 .append(" = ")
+                                 .append(el.getValue())
+                                 .append("\n"));
+        LOGGER.trace(sb.toString());
+      }
     }
     return failed;
   }

Add Task.sideEffect()

Existing API to create a side effect is Task.withSideEffect(). It requires an existing Task instance to begin with what leads to unnatural constructs such as: Task.value(null).withSideEffect().
ParSeq should provide an API for creating side effect in situations where there is no existing Task instance.

Improve generation of task descriptions for deeply nested structures

Here is an example that shows the problem:

package com.linkedin.parseq.example.simple;

import static com.linkedin.parseq.example.common.ExampleUtil.printTracingResults;

import java.util.concurrent.ThreadLocalRandom;

import com.linkedin.parseq.Engine;
import com.linkedin.parseq.Task;
import com.linkedin.parseq.example.common.AbstractExample;

public class NestedFlatMap extends AbstractExample {
  public static void main(String[] args) throws Exception {
    new NestedFlatMap().runExample();
  }

  @Override
  protected void doRunExample(final Engine engine) throws Exception {

    Task<String> task1 = Task.value("x");
    Task<String> task2 = Task.value("y");

    Task<?> task =  Task.par(task1, task2).flatMap((t1, t2) -> {
      if (2 < ThreadLocalRandom.current().nextDouble())
        return Task.value("a");
      else
        return Task.value("b").flatMap(x -> {
          if (2 < ThreadLocalRandom.current().nextDouble())
            return Task.value("c");
          else
            return Task.value("d");
        });
    });
    engine.run(task);
    task.await();

    printTracingResults(task);
  }

}

Above program generates the following trace:

{"planId":5,"planClass":"com.linkedin.parseq.Task$1","traces":[{"id":0,"name":"value","resultType":"SUCCESS","hidden":false,"systemHidden":false,"startNanos":7866896119553,"pendingNanos":7866896134176,"endNanos":7866896137271,"taskType":"fusion"},{"id":1,"name":"value","resultType":"SUCCESS","hidden":false,"systemHidden":false,"startNanos":7866892366208,"pendingNanos":7866895918666,"endNanos":7866895954020,"taskType":"fusion"},{"id":2,"name":"par2","resultType":"SUCCESS","hidden":false,"systemHidden":false,"startNanos":7866889586253,"pendingNanos":7866892353402,"endNanos":7866896162677},{"id":3,"name":"async fused","resultType":"SUCCESS","hidden":false,"systemHidden":true,"startNanos":7866882949082,"pendingNanos":7866889564548,"endNanos":7866900766575,"taskType":"fusion"},{"id":4,"name":"flatMap: doRunExample(NestedFlatMap:23)","resultType":"SUCCESS","hidden":false,"systemHidden":false,"startNanos":7866875885363,"pendingNanos":7866882266050,"endNanos":7866904960272,"taskType":"flatten"},{"id":1000,"name":"flatMap: doRunExample(NestedFlatMap:23)","resultType":"SUCCESS","hidden":false,"systemHidden":true,"startNanos":7866898291297,"pendingNanos":7866900740409,"endNanos":7866900745536},{"id":1001,"name":"value","resultType":"SUCCESS","hidden":false,"systemHidden":false,"startNanos":7866901608010,"pendingNanos":7866904075956,"endNanos":7866904091911,"taskType":"fusion"},{"id":1002,"name":"fused","resultType":"SUCCESS","hidden":false,"systemHidden":true,"startNanos":7866900900638,"pendingNanos":7866904836592,"endNanos":7866904841609,"taskType":"fusion"},{"id":1003,"name":"flatMap: accept(PromiseTransformer:27)","resultType":"SUCCESS","hidden":false,"systemHidden":false,"startNanos":7866900867101,"pendingNanos":7866900891546,"endNanos":7866904942087,"taskType":"flatten"},{"id":1004,"name":"flatMap: accept(PromiseTransformer:27)","resultType":"SUCCESS","hidden":false,"systemHidden":true,"startNanos":7866904765071,"pendingNanos":7866904811345,"endNanos":7866904811345},{"id":1005,"name":"value","resultType":"SUCCESS","hidden":false,"systemHidden":false,"startNanos":7866904894938,"pendingNanos":7866904928449,"endNanos":7866904931534,"taskType":"fusion"}],"relationships":[{"relationship":"PARENT_OF","from":3,"to":1000},{"relationship":"PARENT_OF","from":1003,"to":1005},{"relationship":"PARENT_OF","from":1002,"to":1004},{"relationship":"PARENT_OF","from":4,"to":3},{"relationship":"SUCCESSOR_OF","from":1003,"to":3},{"relationship":"SUCCESSOR_OF","from":1000,"to":2},{"relationship":"PARENT_OF","from":4,"to":1003},{"relationship":"SUCCESSOR_OF","from":1004,"to":1001},{"relationship":"PARENT_OF","from":2,"to":1},{"relationship":"PARENT_OF","from":2,"to":0},{"relationship":"PARENT_OF","from":3,"to":2},{"relationship":"PARENT_OF","from":1003,"to":1002},{"relationship":"PARENT_OF","from":1002,"to":1001},{"relationship":"SUCCESSOR_OF","from":1005,"to":1002}]}

Deeply nested flatMap has description: "flatMap: accept(PromiseTransformer:33)". Ideally it should say: "flatMap: doRunExample(FlatMap:33)".

Why does ShallowTrace not allow value string for UNFINISHED and EARLY_FINISH ResultType?

When creating an instance of the ShallowTrace class (using the ShallowTraceBuilder), two of the constructor parameters are ResultType resultType and String value. The value parameter seems like the most appropriate place to put some String representation of the result of an asynchronous operation or an error message if the operation failed.

However, the ShallowTrace class throws an exception if you try to set a value when using it with ResultType.EARLY_FINISH and ResultType.UNFINISHED. The problem with this is that there are cases where you'd want to set the value parameter to some specific message but still use a ResultType or EARLY_FINISH or UNFINISHED. For example, when some operation times out, I'd want ResultType to be EARLY_FINISH and the value to be the message from the TimeoutException, such as "this action exceeded its SLA of 500ms".

Is it possible to remove the "is value set" check from ShallowTrace or is it actually protecting against something I'm overlooking?

Identify source of the exception in a task trace

In ParSeq exceptions are automatically propagated. It means that within the trace there are typically many failed tasks with the same exception.
It would be very useful to identify which task was a source of the exception to help troubleshooting problems.

CI Failure: TestContext.testCreateTimer

Looks like an intermittent failure, found by Travis-CI. I suspect we may need to bump up the tolerance on the timeouts.

277   [testng] FAILED: testCreateTimer
278   [testng] java.lang.AssertionError: 
279   [testng]  at org.testng.AssertJUnit.fail(AssertJUnit.java:59)
280   [testng]  at org.testng.AssertJUnit.assertTrue(AssertJUnit.java:24)
281   [testng]  at org.testng.AssertJUnit.assertTrue(AssertJUnit.java:33)
282   [testng]  at com.linkedin.parseq.TestContext.testCreateTimer(TestContext.java:138)
283   [testng]  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
284   [testng]  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
285   [testng]  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
286   [testng]  at java.lang.reflect.Method.invoke(Method.java:601)
287   [testng]  at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:80)
288   [testng]  at org.testng.internal.Invoker.invokeMethod(Invoker.java:701)
289   [testng]  at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:893)
290   [testng]  at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1218)
291   [testng]  at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:127)
292   [testng]  at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:111)
293   [testng]  at org.testng.TestRunner.privateRun(TestRunner.java:758)
294   [testng]  at org.testng.TestRunner.run(TestRunner.java:613)
295   [testng]  at org.testng.SuiteRunner.runTest(SuiteRunner.java:334)
296   [testng]  at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:329)
297   [testng]  at org.testng.SuiteRunner.privateRun(SuiteRunner.java:291)
298   [testng]  at org.testng.SuiteRunner.run(SuiteRunner.java:240)
299   [testng]  at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:53)
300   [testng]  at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:87)
301   [testng]  at org.testng.TestNG.runSuitesSequentially(TestNG.java:1170)
302   [testng]  at org.testng.TestNG.runSuitesLocally(TestNG.java:1095)
303   [testng]  at org.testng.TestNG.run(TestNG.java:1007)
304   [testng]  at org.testng.TestNG.privateMain(TestNG.java:1315)
305   [testng]  at org.testng.TestNG.main(TestNG.java:1279)

Tasks.<T>par(emptyIterable<? extends Task<? extends T>>) should not throw IllegalArgumentException

It should be ok to call Tasks.par(tasks) with tasks being an empty collection. When run, such parallel task's Promise would resolve to an empty List.
Currently client code has to check if task accumulator is empty and arrange different composition of tasks instead:

{
  Collection<Task<Foo>> fooTasks = buildFooTasks(...); // may return empty collection
  Task<Bar> mergeFoosTask = new MergeFoosTask();
  Task<Bar> computeAndMerge = fooTasks.isEmpty ? mergeFoosTask : Tasks.seq(Tasks.par(fooTasks), mergeFoosTask);
  ...
}

Make BaseEngineTest behave as BaseEngineParTest by default

When BaseEngineTest is used in multithreaded unit test it will throw exception explaining that perhaps BaseEngineParTest shoul dbe used instead.
It would be better however to make it default by simply renaming:
BaseEngineTest -> BaseIsolatedEngineTest
BaseEngineParTest -> BaseEngineTest
BaseEngineParTest should be just alias to BaseEngineTest and left for backwards compatibility.

Create ParSeqUnitTestHelper to avoid extending BaseEngineTest

Sometimes it is not convenient / possible to extend BaseEngineTest. All functionality can be moved to a ParSeqUnitTestHelper class that can be instantiated and used from a unit test class that does not extend BaseEngineTest. BaseEngineTest can delegate to ParSeqUnitTestHelper.

Split existing batches according to maxBatchSize configuration in ParSeqRestClient

Currently maxBatchSize does not affect existing batch GET requests. It is only used to limit size of batch requests created as a result of aggregation, see:

if (_batchSize == 0 || (safeToAddWithoutOverflow(_batchSize, size) && _batchSize + size <= _maxSize)) {

This change is to make sure that all outgoing batch GET requests have size below maxBatchSize even if original batch GET request had a larger size.

Improve exception when function passed to one of Task's API that is supposed to return Task returns null

For example:

Task.value(10).flatMap(x -> null);

Above task is resolved with the following failure that does not explain what the problem was:

java.lang.NullPointerException
	at com.linkedin.parseq.promise.Promises.propagateResult(Promises.java:86)
	at com.linkedin.parseq.promise.Promises.propagateResult(Promises.java:90)
	at com.linkedin.parseq.Task.lambda$22(Task.java:825)
	at com.linkedin.parseq.BaseTask$WrappedContext$WrappedAfter.lambda$0(BaseTask.java:528)
	at com.linkedin.parseq.internal.ContextImpl$4$2.onResolved(ContextImpl.java:164)
	at com.linkedin.parseq.promise.SettablePromiseImpl.notifyListener(SettablePromiseImpl.java:148)
	at com.linkedin.parseq.promise.SettablePromiseImpl.notifyListeners(SettablePromiseImpl.java:136)
	at com.linkedin.parseq.promise.SettablePromiseImpl.lambda$0(SettablePromiseImpl.java:117)
	at com.linkedin.parseq.internal.Continuations$Continuation.loop(Continuations.java:78)
	at com.linkedin.parseq.internal.Continuations$Continuation.submit(Continuations.java:62)
	at com.linkedin.parseq.internal.Continuations$Continuation.access$1(Continuations.java:56)
	at com.linkedin.parseq.internal.Continuations.submit(Continuations.java:41)
	at com.linkedin.parseq.promise.SettablePromiseImpl.doFinish(SettablePromiseImpl.java:117)
	at com.linkedin.parseq.promise.SettablePromiseImpl.done(SettablePromiseImpl.java:51)
	at com.linkedin.parseq.BaseTask.done(BaseTask.java:294)
	at com.linkedin.parseq.BaseTask.lambda$0(BaseTask.java:187)
	at com.linkedin.parseq.promise.SettablePromiseImpl.notifyListener(SettablePromiseImpl.java:148)
	at com.linkedin.parseq.promise.SettablePromiseImpl.addListener(SettablePromiseImpl.java:102)
	at com.linkedin.parseq.BaseTask.contextRun(BaseTask.java:183)
	at com.linkedin.parseq.internal.ContextImpl$2.run(ContextImpl.java:93)
	at com.linkedin.parseq.internal.SerialExecutor$ExecutorLoop.run(SerialExecutor.java:121)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)

Log blocking tasks

Blocking tasks lead to thread starvation and complete halt of an Engine.
Rate limited logging of tasks that take longer than certain time threshold can help troubleshooting such situations.

Waterfall view should have dynamic width for SVG DOM

The DOM element for the Waterfall view is statically set to 1220px.

var WIDTH = 960, MARGIN = {
top : 40,
bottom : 10,
left : 10,
right : 250
}

var svg = root.append('svg')
.attr('width', WIDTH + MARGIN.left + MARGIN.right), vis = svg.append('g')
.attr('transform', 'translate(' + MARGIN.left + ',' + MARGIN.top + ')');

This causes a problem when tracing a very complicated call as it crops content once it exceeds the pre-set 1220px width.

It would be very helpful if this width was screen dependent and/or the overflow on the SVG would be handled by a scrollable container or some sort.

Need an easy way to return the value of a single task in `par`

We have a use case where we have 3 tasks:

  • t1 produces some value
  • t2 is dependent on t1 and produces a value
  • t3 is dependent on t1, but does not produce a value

We'd like our composite task to return the result of t2. We could sequence them like:

Tasks.seq(t1, t3, t2)

However, t2 and t3 have no inter-dependency, so we'd sacrifice parallelism unnecessarily. Ideally, we should be able to parallelize these tasks without requiring a custom task. I propose the following way of composing these tasks:

Tasks.seq(t1, Tasks.par(t2, t3), Tasks.value(t2))

This requires that we add Tasks.value as a new primitive in ParSeq. It effectively acts as a hint about the return value: when it is run it just returns the value of another task.

Investigate usage of Task.value(null)

Task.value(null) shows up in real code in places where there are deficiencies in ParSeq's API, see #132 or where it is not clear which existing API is supposed to be used.
This work is to investigate usage of Task.value(null) and if possible improve ParSeq API to avoid it's usage.

Time based batching aggregation

Currently batching is using the following heuristic to decide when an aggregation action should be executed: aggregate all outstanding batchable tasks when plan queue is empty and there is no task that can make immediate progress. While this is intended behavior in many situations, sometimes different mechanism is required e.g. time based.
This task is to consider and implement time based batching aggregation.

Clarify when parallel tasks are actually executed in parallel

The documentation should be clarified on what parallel execution means. Coming from parallel computing I assume that parallel execution would mean that a task would run concurrent with other tasks. However parallel seems to have a different meaning in ParSeq.

The documentation have the following statement

We can use Task.par() method to compose tasks to run in parallel

The tasks are executed concurrently if the tasks have been created as blocking tasks. They are however all executed one at a time if they are composed of callables. This can be easily seen by viewing the Graphviz Timeline of the MergeSort example. None of the parallel operations are executed concurrently.

Improve Task sharing

Investigate the following strategy: instead of using Task.shareable() automatically detect if task has more than on parent and if this is the case then cancel task only if all parents are resolved.

Auto-run the Task that is returned from Task.run

This has been raised as a surprise by several people - most recently by @jhartman. The behavior of ParSeq differs between core ParSeq, Play, and Restli. ParSeq and Play require Tasks to be explicitly run. Restli auto-runs the returned Task.

In an environment where users are using seq / par, the auto-run behavior is most intuitive. In an environment where users are using context.after, the opposite is true. Here's an example where auto-run would do the wrong thing:

    final Task<String> parent = new BaseTask<String>()
    {
      @Override
      protected Promise<? extends String> run(final Context context) throws Exception
      {
        context.after(a).run(b);
        context.after(a).run(c);
        context.after(b, c).run(d);
        context.run(a);
        return d;
      }
    };

With auto run we'd try to run d before b and c. However, context.after is intended as a low-level construct for building up more user friendly tasks, such as the above mentioned seq and par, so it seems better to make run handling special for context.after and not the other way around.

Expand all / collapse all in waterfall view

From @yborovikov:

Current behavior is to collapse / expand clicked task only. when the task is expanded back, all descendants are nicely restored to their previous states. it would be handy to have a way to forcefully expand (or collapse) all descendants regardless of their previous states.

Investigate if strong references between tasks can be avoided.

The following example leads to OOM because of chain of references.

public class RetentionExample extends AbstractExample {

  private Task<String> last = null;

  public static void main(String[] args) throws Exception {
      new RetentionExample2().runExample();
  }

  @Override
  protected void doRunExample(final Engine engine) throws Exception {

    for (int i = 0; i < 1000000; i++) {
      Task<String> t = null;
      if (last != null) {
        t = last.andThen(Task.callable(() -> ""));
        engine.run(t);
        t.await();
      } else {
        t = Task.callable(() -> "");
      }
      last = t;
    }

    System.out.println(last.getTrace());
  }
}

trace.html should use html, head, and body tags

Just noticed this today, but the tracevis trace.html does not use <html>, <head>, or <body> tags. Browsers are very forgiving, so it still renders, but it seems like the wrong way to go about it and may cause weird rendering and browser compatibility issues in the future.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.