{"action":"created","comment":{"url":"https://api.github.com/repos/linkedin/parseq/pulls/comments/43945236","id":43945236,"diff_hunk":"@@ -57,56 +85,144 @@ private void trnasitionToDone(final FusionTraceContext traceContext) {\n     transitionDone();\n   }\n \n-  private void addRelationships(final FusionTraceContext traceContext) {\n-    TraceBuilder builder = getTraceBuilder();\n-    final ShallowTraceBuilder parent = traceContext.getContext().getShallowTraceBuilder();\n-    final boolean isTrigger = traceContext.getTrigger().getId().equals(getId());\n+  /**\n+   * Has current task initiated propagation?\n+   */\n+  private boolean isPropagationInitiator(final FusionTraceContext traceContext) {\n+    return traceContext.getPropagationInitiator().getId().equals(getId());\n+  }\n \n-    if (!isTrigger) {\n-      builder.addRelationship(Relationship.PARENT_OF, parent, _shallowTraceBuilder);\n+  /**\n+   * Depending on the context, returns trace builder that will contain information about the\n+   * transformation. This is used to avoid wrapping single transformation with \"fused\" parent.\n+   */\n+  private ShallowTraceBuilder getEffectiveShallowTraceBuilder(final FusionTraceContext traceContext) {\n+    if (isPropagationInitiator(traceContext)) {\n+      return traceContext.getSurrogate();\n+    } else {\n+      return _shallowTraceBuilder;\n     }\n+  }\n \n-    if (_predecessor != null && (!isTrigger)) {\n-      builder.addRelationship(Relationship.SUCCESSOR_OF, _shallowTraceBuilder, _predecessor);\n+  private void addRelationships(final FusionTraceContext traceContext) {\n+    final ShallowTraceBuilder effectoveShallowTraceBuilder = getEffectiveShallowTraceBuilder(traceContext);\n+    TraceBuilder builder = getTraceBuilder();\n+    builder.addRelationship(Relationship.PARENT_OF, traceContext.getParent().getShallowTraceBuilder(), effectoveShallowTraceBuilder);\n+    if (_predecessor != null) {\n+      builder.addRelationship(Relationship.SUCCESSOR_OF, effectoveShallowTraceBuilder, _predecessor);\n     }\n   }\n \n   private void addPotentialRelationships(final FusionTraceContext traceContext, final TraceBuilder builder) {\n-    final ShallowTraceBuilder parent = traceContext.getContext().getShallowTraceBuilder();\n-    final boolean isTrigger = traceContext.getTrigger().getId().equals(getId());\n-\n-    if (!isTrigger) {\n-      builder.addRelationship(Relationship.POTENTIAL_CHILD_OF, _shallowTraceBuilder, parent);\n+    final ShallowTraceBuilder effectoveShallowTraceBuilder = getEffectiveShallowTraceBuilder(traceContext);\n+    builder.addRelationship(Relationship.POTENTIAL_CHILD_OF, effectoveShallowTraceBuilder, traceContext.getParent().getShallowTraceBuilder());\n+    if (_predecessor != null) {\n+      builder.addRelationship(Relationship.POSSIBLE_SUCCESSOR_OF, effectoveShallowTraceBuilder, _predecessor);\n     }\n+  }\n \n-    if (_predecessor != null && (!isTrigger)) {\n-      builder.addRelationship(Relationship.POSSIBLE_SUCCESSOR_OF, _shallowTraceBuilder, _predecessor);\n-    }\n+  /**\n+   * Composes transformation with the transformation of the predecessor.\n+   */\n+  private <R> Consumer3<FusionTraceContext, Promise<S>, Settable<T>> compose(\n+      final Consumer3<FusionTraceContext, Promise<S>, Settable<R>> predecessor,\n+      final Consumer3<FusionTraceContext, Promise<R>, Settable<T>> propagator) {\n+\n+    return (traceContext, src, dst) -> {\n+\n+      /*\n+       * At this point we know that transformation chain's length > 1.\n+       * This code is executed during task execution, not when task is constructed.\n+       */\n+      traceContext.createSurrogate();\n+\n+      predecessor.accept(traceContext, src, new Settable<R>() {\n+\n+        @Override\n+        public void done(R value) throws PromiseResolvedException {\n+          try {\n+            /* Track start time of the transformation. End time is tracked by closure created by completing() */\n+            getEffectiveShallowTraceBuilder(traceContext).setStartNanos(System.nanoTime());\n+            propagator.accept(traceContext, Promises.value(value), dst);\n+          } catch (Exception e) {\n+            /* This can only happen if there is an internal problem. Propagators should not throw any exceptions. */\n+            LOGGER.error(\"ParSeq ingternal error. An exception was thrown by propagator\", e);\n+          }\n+        }\n+\n+        @Override\n+        public void fail(Throwable error) throws PromiseResolvedException {\n+          try {\n+            /* Track start time of the transformation. End time is tracked by closure created by completing() */\n+            getEffectiveShallowTraceBuilder(traceContext).setStartNanos(System.nanoTime());\n+            propagator.accept(traceContext, Promises.error(error), dst);\n+          } catch (Exception e) {\n+            /* This can only happen if there is an internal problem. Propagators should not throw any exceptions. */\n+            LOGGER.error(\"ParSeq ingternal error. An exception was thrown by propagator.\", e);\n+          }\n+        }\n+      });\n+    };\n   }\n \n+  /**\n+   * Adds closure to the propagator that maintains correct state of the task e.g. transitions between states,\n+   * adds trace etc.\n+   */\n   private Consumer3<FusionTraceContext, Promise<S>, Settable<T>> completing(\n       final Consumer3<FusionTraceContext, Promise<S>, Settable<T>> propagator) {\n     return (traceContext, src, dest) -> {\n       final SettablePromise<T> settable = FusionTask.this.getSettableDelegate();\n \n-      if (traceContext.getTrigger() != null && traceContext.getTrigger().getId().equals(getId())) {\n-        //simply propagate result because the BaseTask's code\n-        //handles completing the task\n+      if (isPropagationInitiator(traceContext)) {\n+        /* BaseTask's code handles completing the parent task","path":"src/com/linkedin/parseq/FusionTask.java","position":186,"original_position":186,"commit_id":"e967e72c06c4121a65032bab7f25b329bb6c9ed6","original_commit_id":"e967e72c06c4121a65032bab7f25b329bb6c9ed6","user":{"login":"zhenkai","id":447715,"avatar_url":"https://avatars.githubusercontent.com/u/447715?v=3","gravatar_id":"","url":"https://api.github.com/users/zhenkai","html_url":"https://github.com/zhenkai","followers_url":"https://api.github.com/users/zhenkai/followers","following_url":"https://api.github.com/users/zhenkai/following{/other_user}","gists_url":"https://api.github.com/users/zhenkai/gists{/gist_id}","starred_url":"https://api.github.com/users/zhenkai/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zhenkai/subscriptions","organizations_url":"https://api.github.com/users/zhenkai/orgs","repos_url":"https://api.github.com/users/zhenkai/repos","events_url":"https://api.github.com/users/zhenkai/events{/privacy}","received_events_url":"https://api.github.com/users/zhenkai/received_events","type":"User","site_admin":false},"body":"Just to be sure, parent task means the source task & surrogate task means the fusion tasks that starts the propagation, right?","created_at":"2015-11-04T21:52:23Z","updated_at":"2015-11-04T21:52:23Z","html_url":"https://github.com/linkedin/parseq/pull/65#discussion_r43945236","pull_request_url":"https://api.github.com/repos/linkedin/parseq/pulls/65","_links":{"self":{"href":"https://api.github.com/repos/linkedin/parseq/pulls/comments/43945236"},"html":{"href":"https://github.com/linkedin/parseq/pull/65#discussion_r43945236"},"pull_request":{"href":"https://api.github.com/repos/linkedin/parseq/pulls/65"}}},"pull_request":{"url":"https://api.github.com/repos/linkedin/parseq/pulls/65","id":49728493,"html_url":"https://github.com/linkedin/parseq/pull/65","diff_url":"https://github.com/linkedin/parseq/pull/65.diff","patch_url":"https://github.com/linkedin/parseq/pull/65.patch","issue_url":"https://api.github.com/repos/linkedin/parseq/issues/65","number":65,"state":"open","locked":false,"title":"Trace improvements","user":{"login":"jodzga","id":2935153,"avatar_url":"https://avatars.githubusercontent.com/u/2935153?v=3","gravatar_id":"","url":"https://api.github.com/users/jodzga","html_url":"https://github.com/jodzga","followers_url":"https://api.github.com/users/jodzga/followers","following_url":"https://api.github.com/users/jodzga/following{/other_user}","gists_url":"https://api.github.com/users/jodzga/gists{/gist_id}","starred_url":"https://api.github.com/users/jodzga/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jodzga/subscriptions","organizations_url":"https://api.github.com/users/jodzga/orgs","repos_url":"https://api.github.com/users/jodzga/repos","events_url":"https://api.github.com/users/jodzga/events{/privacy}","received_events_url":"https://api.github.com/users/jodzga/received_events","type":"User","site_admin":false},"body":"Tracing improvements:\r\n  - Changed the way tasks fusion is reflected in trace so that it is  more intuitive and better reflect performance of individual transformations\r\n  - Added class name of lambdas passed to functional API to default task descriptions\r\n","created_at":"2015-11-04T18:47:39Z","updated_at":"2015-11-04T21:52:23Z","closed_at":null,"merged_at":null,"merge_commit_sha":"1ff578475eda36cd43ed53b9b9866d8da477a43e","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/linkedin/parseq/pulls/65/commits","review_comments_url":"https://api.github.com/repos/linkedin/parseq/pulls/65/comments","review_comment_url":"https://api.github.com/repos/linkedin/parseq/pulls/comments{/number}","comments_url":"https://api.github.com/repos/linkedin/parseq/issues/65/comments","statuses_url":"https://api.github.com/repos/linkedin/parseq/statuses/e967e72c06c4121a65032bab7f25b329bb6c9ed6","head":{"label":"jodzga:fusion-trace-improvements","ref":"fusion-trace-improvements","sha":"e967e72c06c4121a65032bab7f25b329bb6c9ed6","user":{"login":"jodzga","id":2935153,"avatar_url":"https://avatars.githubusercontent.com/u/2935153?v=3","gravatar_id":"","url":"https://api.github.com/users/jodzga","html_url":"https://github.com/jodzga","followers_url":"https://api.github.com/users/jodzga/followers","following_url":"https://api.github.com/users/jodzga/following{/other_user}","gists_url":"https://api.github.com/users/jodzga/gists{/gist_id}","starred_url":"https://api.github.com/users/jodzga/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jodzga/subscriptions","organizations_url":"https://api.github.com/users/jodzga/orgs","repos_url":"https://api.github.com/users/jodzga/repos","events_url":"https://api.github.com/users/jodzga/events{/privacy}","received_events_url":"https://api.github.com/users/jodzga/received_events","type":"User","site_admin":false},"repo":{"id":24863397,"name":"parseq","full_name":"jodzga/parseq","owner":{"login":"jodzga","id":2935153,"avatar_url":"https://avatars.githubusercontent.com/u/2935153?v=3","gravatar_id":"","url":"https://api.github.com/users/jodzga","html_url":"https://github.com/jodzga","followers_url":"https://api.github.com/users/jodzga/followers","following_url":"https://api.github.com/users/jodzga/following{/other_user}","gists_url":"https://api.github.com/users/jodzga/gists{/gist_id}","starred_url":"https://api.github.com/users/jodzga/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jodzga/subscriptions","organizations_url":"https://api.github.com/users/jodzga/orgs","repos_url":"https://api.github.com/users/jodzga/repos","events_url":"https://api.github.com/users/jodzga/events{/privacy}","received_events_url":"https://api.github.com/users/jodzga/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jodzga/parseq","description":"Asynchronous Java made easier","fork":true,"url":"https://api.github.com/repos/jodzga/parseq","forks_url":"https://api.github.com/repos/jodzga/parseq/forks","keys_url":"https://api.github.com/repos/jodzga/parseq/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jodzga/parseq/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jodzga/parseq/teams","hooks_url":"https://api.github.com/repos/jodzga/parseq/hooks","issue_events_url":"https://api.github.com/repos/jodzga/parseq/issues/events{/number}","events_url":"https://api.github.com/repos/jodzga/parseq/events","assignees_url":"https://api.github.com/repos/jodzga/parseq/assignees{/user}","branches_url":"https://api.github.com/repos/jodzga/parseq/branches{/branch}","tags_url":"https://api.github.com/repos/jodzga/parseq/tags","blobs_url":"https://api.github.com/repos/jodzga/parseq/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jodzga/parseq/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jodzga/parseq/git/refs{/sha}","trees_url":"https://api.github.com/repos/jodzga/parseq/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jodzga/parseq/statuses/{sha}","languages_url":"https://api.github.com/repos/jodzga/parseq/languages","stargazers_url":"https://api.github.com/repos/jodzga/parseq/stargazers","contributors_url":"https://api.github.com/repos/jodzga/parseq/contributors","subscribers_url":"https://api.github.com/repos/jodzga/parseq/subscribers","subscription_url":"https://api.github.com/repos/jodzga/parseq/subscription","commits_url":"https://api.github.com/repos/jodzga/parseq/commits{/sha}","git_commits_url":"https://api.github.com/repos/jodzga/parseq/git/commits{/sha}","comments_url":"https://api.github.com/repos/jodzga/parseq/comments{/number}","issue_comment_url":"https://api.github.com/repos/jodzga/parseq/issues/comments{/number}","contents_url":"https://api.github.com/repos/jodzga/parseq/contents/{+path}","compare_url":"https://api.github.com/repos/jodzga/parseq/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jodzga/parseq/merges","archive_url":"https://api.github.com/repos/jodzga/parseq/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jodzga/parseq/downloads","issues_url":"https://api.github.com/repos/jodzga/parseq/issues{/number}","pulls_url":"https://api.github.com/repos/jodzga/parseq/pulls{/number}","milestones_url":"https://api.github.com/repos/jodzga/parseq/milestones{/number}","notifications_url":"https://api.github.com/repos/jodzga/parseq/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jodzga/parseq/labels{/name}","releases_url":"https://api.github.com/repos/jodzga/parseq/releases{/id}","created_at":"2014-10-06T20:25:46Z","updated_at":"2015-02-17T22:27:28Z","pushed_at":"2015-11-04T18:45:33Z","git_url":"git://github.com/jodzga/parseq.git","ssh_url":"git@github.com:jodzga/parseq.git","clone_url":"https://github.com/jodzga/parseq.git","svn_url":"https://github.com/jodzga/parseq","homepage":"","size":1956,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"linkedin:master","ref":"master","sha":"6f2a5b7c8db4c1e131580fc995e42efb34985628","user":{"login":"linkedin","id":357098,"avatar_url":"https://avatars.githubusercontent.com/u/357098?v=3","gravatar_id":"","url":"https://api.github.com/users/linkedin","html_url":"https://github.com/linkedin","followers_url":"https://api.github.com/users/linkedin/followers","following_url":"https://api.github.com/users/linkedin/following{/other_user}","gists_url":"https://api.github.com/users/linkedin/gists{/gist_id}","starred_url":"https://api.github.com/users/linkedin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/linkedin/subscriptions","organizations_url":"https://api.github.com/users/linkedin/orgs","repos_url":"https://api.github.com/users/linkedin/repos","events_url":"https://api.github.com/users/linkedin/events{/privacy}","received_events_url":"https://api.github.com/users/linkedin/received_events","type":"Organization","site_admin":false},"repo":{"id":5859444,"name":"parseq","full_name":"linkedin/parseq","owner":{"login":"linkedin","id":357098,"avatar_url":"https://avatars.githubusercontent.com/u/357098?v=3","gravatar_id":"","url":"https://api.github.com/users/linkedin","html_url":"https://github.com/linkedin","followers_url":"https://api.github.com/users/linkedin/followers","following_url":"https://api.github.com/users/linkedin/following{/other_user}","gists_url":"https://api.github.com/users/linkedin/gists{/gist_id}","starred_url":"https://api.github.com/users/linkedin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/linkedin/subscriptions","organizations_url":"https://api.github.com/users/linkedin/orgs","repos_url":"https://api.github.com/users/linkedin/repos","events_url":"https://api.github.com/users/linkedin/events{/privacy}","received_events_url":"https://api.github.com/users/linkedin/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/linkedin/parseq","description":"Asynchronous Java made easier","fork":false,"url":"https://api.github.com/repos/linkedin/parseq","forks_url":"https://api.github.com/repos/linkedin/parseq/forks","keys_url":"https://api.github.com/repos/linkedin/parseq/keys{/key_id}","collaborators_url":"https://api.github.com/repos/linkedin/parseq/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/linkedin/parseq/teams","hooks_url":"https://api.github.com/repos/linkedin/parseq/hooks","issue_events_url":"https://api.github.com/repos/linkedin/parseq/issues/events{/number}","events_url":"https://api.github.com/repos/linkedin/parseq/events","assignees_url":"https://api.github.com/repos/linkedin/parseq/assignees{/user}","branches_url":"https://api.github.com/repos/linkedin/parseq/branches{/branch}","tags_url":"https://api.github.com/repos/linkedin/parseq/tags","blobs_url":"https://api.github.com/repos/linkedin/parseq/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/linkedin/parseq/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/linkedin/parseq/git/refs{/sha}","trees_url":"https://api.github.com/repos/linkedin/parseq/git/trees{/sha}","statuses_url":"https://api.github.com/repos/linkedin/parseq/statuses/{sha}","languages_url":"https://api.github.com/repos/linkedin/parseq/languages","stargazers_url":"https://api.github.com/repos/linkedin/parseq/stargazers","contributors_url":"https://api.github.com/repos/linkedin/parseq/contributors","subscribers_url":"https://api.github.com/repos/linkedin/parseq/subscribers","subscription_url":"https://api.github.com/repos/linkedin/parseq/subscription","commits_url":"https://api.github.com/repos/linkedin/parseq/commits{/sha}","git_commits_url":"https://api.github.com/repos/linkedin/parseq/git/commits{/sha}","comments_url":"https://api.github.com/repos/linkedin/parseq/comments{/number}","issue_comment_url":"https://api.github.com/repos/linkedin/parseq/issues/comments{/number}","contents_url":"https://api.github.com/repos/linkedin/parseq/contents/{+path}","compare_url":"https://api.github.com/repos/linkedin/parseq/compare/{base}...{head}","merges_url":"https://api.github.com/repos/linkedin/parseq/merges","archive_url":"https://api.github.com/repos/linkedin/parseq/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/linkedin/parseq/downloads","issues_url":"https://api.github.com/repos/linkedin/parseq/issues{/number}","pulls_url":"https://api.github.com/repos/linkedin/parseq/pulls{/number}","milestones_url":"https://api.github.com/repos/linkedin/parseq/milestones{/number}","notifications_url":"https://api.github.com/repos/linkedin/parseq/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/linkedin/parseq/labels{/name}","releases_url":"https://api.github.com/repos/linkedin/parseq/releases{/id}","created_at":"2012-09-18T16:43:59Z","updated_at":"2015-11-04T16:43:49Z","pushed_at":"2015-11-04T18:47:39Z","git_url":"git://github.com/linkedin/parseq.git","ssh_url":"git@github.com:linkedin/parseq.git","clone_url":"https://github.com/linkedin/parseq.git","svn_url":"https://github.com/linkedin/parseq","homepage":"","size":4506,"stargazers_count":341,"watchers_count":341,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":83,"mirror_url":null,"open_issues_count":14,"forks":83,"open_issues":14,"watchers":341,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/linkedin/parseq/pulls/65"},"html":{"href":"https://github.com/linkedin/parseq/pull/65"},"issue":{"href":"https://api.github.com/repos/linkedin/parseq/issues/65"},"comments":{"href":"https://api.github.com/repos/linkedin/parseq/issues/65/comments"},"review_comments":{"href":"https://api.github.com/repos/linkedin/parseq/pulls/65/comments"},"review_comment":{"href":"https://api.github.com/repos/linkedin/parseq/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/linkedin/parseq/pulls/65/commits"},"statuses":{"href":"https://api.github.com/repos/linkedin/parseq/statuses/e967e72c06c4121a65032bab7f25b329bb6c9ed6"}}}}