You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ai...@apache.org on 2019/08/28 20:22:34 UTC

[nifi] branch analytics-framework updated (9e35e4c -> 760ee69)

This is an automated email from the ASF dual-hosted git repository.

aichrist pushed a change to branch analytics-framework
in repository https://gitbox.apache.org/repos/asf/nifi.git.


    from 9e35e4c  NIFI-6586 - documentation and comments
     new b74c4df  NIFI-6568 - Surface time-to-back-pressure and initial predictions in the UI * Add multi-line tooltips with detail for connection queue back pressure graphics. * Add estimated time to back pressure to connections summary table. * Add back pressure prediction ticks. * add moment.js to format predicted time to back pressure * tweak summary table headings to match data displayed. re-order connection summary columns
     new 760ee69  NIFI-6568 - Properly sort the min estimated time to back pressure in the connection summary table. Also added a js doc comment.

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 nifi-assembly/LICENSE                              |  25 ++
 .../src/main/resources/META-INF/LICENSE            |  25 ++
 .../nifi-framework/nifi-web/nifi-web-ui/pom.xml    |   4 +
 .../src/main/frontend/package-lock.json            |  21 +-
 .../nifi-web-ui/src/main/frontend/package.json     |   1 +
 .../src/main/resources/META-INF/LICENSE            |  25 ++
 .../main/webapp/WEB-INF/pages/bulletin-board.jsp   |   1 +
 .../src/main/webapp/WEB-INF/pages/canvas.jsp       |   2 +
 .../src/main/webapp/WEB-INF/pages/cluster.jsp      |   1 +
 .../src/main/webapp/WEB-INF/pages/counters.jsp     |   1 +
 .../src/main/webapp/WEB-INF/pages/history.jsp      |   1 +
 .../src/main/webapp/WEB-INF/pages/login.jsp        |   1 +
 .../src/main/webapp/WEB-INF/pages/provenance.jsp   |   1 +
 .../src/main/webapp/WEB-INF/pages/summary.jsp      |   1 +
 .../src/main/webapp/WEB-INF/pages/templates.jsp    |   1 +
 .../src/main/webapp/WEB-INF/pages/users.jsp        |   1 +
 .../nifi-web-ui/src/main/webapp/css/graph.css      |  25 +-
 .../src/main/webapp/js/nf/canvas/nf-connection.js  | 333 +++++++++++++++++----
 .../nifi-web-ui/src/main/webapp/js/nf/nf-common.js |  28 +-
 .../main/webapp/js/nf/summary/nf-summary-table.js  | 152 +++++++---
 20 files changed, 543 insertions(+), 107 deletions(-)


[nifi] 02/02: NIFI-6568 - Properly sort the min estimated time to back pressure in the connection summary table. Also added a js doc comment.

Posted by ai...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

aichrist pushed a commit to branch analytics-framework
in repository https://gitbox.apache.org/repos/asf/nifi.git

commit 760ee6920c6d4df44e7d2053ac0b1739c88af920
Author: Rob Fellows <ro...@gmail.com>
AuthorDate: Wed Aug 28 10:54:19 2019 -0400

    NIFI-6568 - Properly sort the min estimated time to back pressure in the connection summary table. Also added a js doc comment.
---
 .../nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js  |  6 ++++++
 .../src/main/webapp/js/nf/summary/nf-summary-table.js        | 12 ++++++++++++
 2 files changed, 18 insertions(+)

diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
index 13ee565..91d398a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
@@ -1281,6 +1281,12 @@
             }
         },
 
+        /**
+         * Formats a number (in milliseconds) to a human-readable textual description.
+         *
+         * @param duration number of milliseconds representing the duration
+         * @return {string|*} a human-readable string
+         */
         formatPredictedDuration: function (duration) {
             if (duration === 0) {
                 return 'now';
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js
index 4f02e74..7ba9b71 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js
@@ -2351,8 +2351,20 @@
                     return aPercentUseDataSize - bPercentUseDataSize;
                 }
             } else if (sortDetails.columnId === 'backpressurePrediction') {
+                // if the connection is at backpressure currently, "now" displays and not the estimate. Should account for that when sorting.
+                var aMaxCurrentUsage = Math.max(_.get(a, 'percentUseBytes', 0), _.get(a, 'percentUseCount', 0));
+                var bMaxCurrentUsage = Math.max(_.get(b, 'percentUseBytes', 0), _.get(b, 'percentUseCount', 0));
+
                 var aMinTime = Math.min(_.get(a, 'predictedMillisUntilBytesBackpressure', Number.MAX_VALUE), _.get(a, 'predictedMillisUntilCountBackpressure', Number.MAX_VALUE));
                 var bMinTime = Math.min(_.get(b, 'predictedMillisUntilBytesBackpressure', Number.MAX_VALUE), _.get(b, 'predictedMillisUntilCountBackpressure', Number.MAX_VALUE));
+
+                if (aMaxCurrentUsage >= 100) {
+                    aMinTime = 0;
+                }
+                if (bMaxCurrentUsage >= 100) {
+                    bMinTime = 0;
+                }
+
                 return aMinTime - bMinTime;
             } else if (sortDetails.columnId === 'sent' || sortDetails.columnId === 'received' || sortDetails.columnId === 'input' || sortDetails.columnId === 'output' || sortDetails.columnId === 'transferred') {
                 var aSplit = a[sortDetails.columnId].split(/\(([^)]+)\)/);


[nifi] 01/02: NIFI-6568 - Surface time-to-back-pressure and initial predictions in the UI * Add multi-line tooltips with detail for connection queue back pressure graphics. * Add estimated time to back pressure to connections summary table. * Add back pressure prediction ticks. * add moment.js to format predicted time to back pressure * tweak summary table headings to match data displayed. re-order connection summary columns

Posted by ai...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

aichrist pushed a commit to branch analytics-framework
in repository https://gitbox.apache.org/repos/asf/nifi.git

commit b74c4dfb8434650aebe350ef90a9e041cbe1980b
Author: Rob Fellows <ro...@gmail.com>
AuthorDate: Mon Jul 22 17:02:48 2019 -0400

    NIFI-6568 - Surface time-to-back-pressure and initial predictions in the UI
    * Add multi-line tooltips with detail for connection queue back pressure graphics.
    * Add estimated time to back pressure to connections summary table.
    * Add back pressure prediction ticks.
    * add moment.js to format predicted time to back pressure
    * tweak summary table headings to match data displayed. re-order connection summary columns
---
 nifi-assembly/LICENSE                              |  25 ++
 .../src/main/resources/META-INF/LICENSE            |  25 ++
 .../nifi-framework/nifi-web/nifi-web-ui/pom.xml    |   4 +
 .../src/main/frontend/package-lock.json            |  21 +-
 .../nifi-web-ui/src/main/frontend/package.json     |   1 +
 .../src/main/resources/META-INF/LICENSE            |  25 ++
 .../main/webapp/WEB-INF/pages/bulletin-board.jsp   |   1 +
 .../src/main/webapp/WEB-INF/pages/canvas.jsp       |   2 +
 .../src/main/webapp/WEB-INF/pages/cluster.jsp      |   1 +
 .../src/main/webapp/WEB-INF/pages/counters.jsp     |   1 +
 .../src/main/webapp/WEB-INF/pages/history.jsp      |   1 +
 .../src/main/webapp/WEB-INF/pages/login.jsp        |   1 +
 .../src/main/webapp/WEB-INF/pages/provenance.jsp   |   1 +
 .../src/main/webapp/WEB-INF/pages/summary.jsp      |   1 +
 .../src/main/webapp/WEB-INF/pages/templates.jsp    |   1 +
 .../src/main/webapp/WEB-INF/pages/users.jsp        |   1 +
 .../nifi-web-ui/src/main/webapp/css/graph.css      |  25 +-
 .../src/main/webapp/js/nf/canvas/nf-connection.js  | 333 +++++++++++++++++----
 .../nifi-web-ui/src/main/webapp/js/nf/nf-common.js |  22 +-
 .../main/webapp/js/nf/summary/nf-summary-table.js  | 140 ++++++---
 20 files changed, 525 insertions(+), 107 deletions(-)

diff --git a/nifi-assembly/LICENSE b/nifi-assembly/LICENSE
index 123125b..eb00732 100644
--- a/nifi-assembly/LICENSE
+++ b/nifi-assembly/LICENSE
@@ -2583,6 +2583,31 @@ This product bundles 'lodash' which is available under an MIT license.
     licenses; we recommend you read them, as their terms may differ from the
     terms above.
 
+This product bundles 'moment' which is available under an MIT license.
+
+    Copyright (c) JS Foundation and other contributors
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use,
+    copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following
+    conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+    OTHER DEALINGS IN THE SOFTWARE.
+
 The binary distribution of this product bundles 'normalize.css'
 
   NORMALIZE.CSS LICENSE
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework-nar/src/main/resources/META-INF/LICENSE b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework-nar/src/main/resources/META-INF/LICENSE
index 679589f..c6530dd 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework-nar/src/main/resources/META-INF/LICENSE
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework-nar/src/main/resources/META-INF/LICENSE
@@ -853,3 +853,28 @@ This product bundles 'lodash' which is available under an MIT license.
     maintained libraries used by this software which have their own
     licenses; we recommend you read them, as their terms may differ from the
     terms above.
+
+This product bundles 'moment' which is available under an MIT license.
+
+    Copyright (c) JS Foundation and other contributors
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use,
+    copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following
+    conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+    OTHER DEALINGS IN THE SOFTWARE.
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
index ef283de..08dca29 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
@@ -239,6 +239,10 @@
                                         <include>lodash-core/distrib/lodash-core.min.js</include>
                                         <include>lodash-core/distrib/README.md</include>
 
+                                        <include>moment/min/moment.min.js</include>
+                                        <include>moment/README.md</include>
+                                        <include>moment/LICENSE</include>
+
                                     </includes>
                                 </resource>
                             </resources>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package-lock.json b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package-lock.json
index 8238e32..9836aa9 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package-lock.json
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package-lock.json
@@ -88,9 +88,9 @@
       }
     },
     "commander": {
-      "version": "2.16.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-2.16.0.tgz",
-      "integrity": "sha512-sVXqklSaotK9at437sFlFpyOcJonxe0yST/AG9DkQKUdIE6IqGIMv4SfAQSKaJbSdVEJYItASCrBiVQHq1HQew=="
+      "version": "2.20.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
+      "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ=="
     },
     "d3": {
       "version": "4.13.0",
@@ -189,8 +189,8 @@
       "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.0.8.tgz",
       "integrity": "sha512-IVCJpQ+YGe3qu6odkPQI0KPqfxkhbP/oM1XhhE/DFiYmcXKfCRub4KXyiuehV1d4drjWVXHUWx4gHqhdZb6n/A==",
       "requires": {
-        "commander": "2.16.0",
-        "iconv-lite": "0.4.23",
+        "commander": "2.20.0",
+        "iconv-lite": "0.4.24",
         "rw": "1.3.3"
       }
     },
@@ -372,9 +372,9 @@
       "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8="
     },
     "iconv-lite": {
-      "version": "0.4.23",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
-      "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
       "requires": {
         "safer-buffer": "2.1.2"
       }
@@ -432,6 +432,11 @@
       "resolved": "https://registry.npmjs.org/lodash-core/-/lodash-core-4.17.11.tgz",
       "integrity": "sha512-8ilprNE67U1REh0wQHL0z37qXdsxuFXjvxehg79Mh/MQgNJ+C1muXtysSKpt9sCxXZUSiwifEC82Vtzf2GSSKQ=="
     },
+    "moment": {
+      "version": "2.24.0",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
+      "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
+    },
     "nomnom": {
       "version": "1.8.1",
       "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz",
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package.json b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package.json
index b45e1a1..2f0fb75 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package.json
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/frontend/package.json
@@ -35,6 +35,7 @@
     "reset.css": "2.0.2",
     "jquery-form": "3.50.0",
     "lodash-core": "4.17.11",
+    "moment": "2.24.0",
     "url-search-params": "0.6.1",
     "jsonlint": "1.6.2",
     "qtip2": "3.0.3",
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/META-INF/LICENSE b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/META-INF/LICENSE
index 9e1b605..fc0572d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/META-INF/LICENSE
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/META-INF/LICENSE
@@ -771,3 +771,28 @@ This product bundles 'lodash' which is available under an MIT license.
     maintained libraries used by this software which have their own
     licenses; we recommend you read them, as their terms may differ from the
     terms above.
+
+This product bundles 'moment' which is available under an MIT license.
+
+    Copyright (c) JS Foundation and other contributors
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use,
+    copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following
+    conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+    OTHER DEALINGS IN THE SOFTWARE.
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/bulletin-board.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/bulletin-board.jsp
index 822e361..879dcb2 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/bulletin-board.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/bulletin-board.jsp
@@ -39,6 +39,7 @@
         <script type="text/javascript" src="js/jquery/jquery.each.js"></script>
         <script type="text/javascript" src="assets/jquery-ui-dist/jquery-ui.min.js"></script>
         <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script>
+        <script type="text/javascript" src="assets/moment/min/moment.min.js"></script>
         <script type="text/javascript" src="assets/qtip2/dist/jquery.qtip.min.js"></script>
         <script type="text/javascript" src="assets/angular/angular.min.js"></script>
         <script type="text/javascript" src="assets/angular-messages/angular-messages.min.js"></script>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
index cee2c5e..4fde541 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
@@ -62,6 +62,7 @@
         <script type="text/javascript" src="js/jquery/modal/jquery.modal.js?${project.version}"></script>
         <script type="text/javascript" src="assets/jquery-minicolors/jquery.minicolors.min.js"></script>
         <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script>
+        <script type="text/javascript" src="assets/moment/min/moment.min.js"></script>
         <script type="text/javascript" src="assets/qtip2/dist/jquery.qtip.min.js"></script>
         <script type="text/javascript" src="assets/slickgrid/lib/jquery.event.drag-2.3.0.js"></script>
         <script type="text/javascript" src="assets/slickgrid/plugins/slick.cellrangeselector.js"></script>
@@ -130,6 +131,7 @@
             <div id="port-tooltips"></div>
             <div id="process-group-tooltips"></div>
             <div id="remote-process-group-tooltips"></div>
+            <div id="connection-tooltips"></div>
         </div>
         <jsp:include page="/WEB-INF/partials/canvas/navigation.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/settings-content.jsp"/>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/cluster.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/cluster.jsp
index 61aa5ca..eb8b0da 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/cluster.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/cluster.jsp
@@ -43,6 +43,7 @@
         <script type="text/javascript" src="js/jquery/jquery.each.js"></script>
         <script type="text/javascript" src="assets/jquery-ui-dist/jquery-ui.min.js"></script>
         <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script>
+        <script type="text/javascript" src="assets/moment/min/moment.min.js"></script>
         <script type="text/javascript" src="assets/qtip2/dist/jquery.qtip.min.js"></script>
         <script type="text/javascript" src="assets/slickgrid/lib/jquery.event.drag-2.3.0.js"></script>
         <script type="text/javascript" src="assets/slickgrid/plugins/slick.cellrangeselector.js"></script>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/counters.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/counters.jsp
index 8c276e3..239f6a6 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/counters.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/counters.jsp
@@ -41,6 +41,7 @@
         <script type="text/javascript" src="js/jquery/jquery.each.js"></script>
         <script type="text/javascript" src="assets/jquery-ui-dist/jquery-ui.min.js"></script>
         <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script>
+        <script type="text/javascript" src="assets/moment/min/moment.min.js"></script>
         <script type="text/javascript" src="assets/qtip2/dist/jquery.qtip.min.js"></script>
         <script type="text/javascript" src="assets/slickgrid/lib/jquery.event.drag-2.3.0.js"></script>
         <script type="text/javascript" src="assets/slickgrid/plugins/slick.cellrangeselector.js"></script>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/history.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/history.jsp
index 429eaf3..47700df 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/history.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/history.jsp
@@ -41,6 +41,7 @@
         <script type="text/javascript" src="js/jquery/jquery.each.js"></script>
         <script type="text/javascript" src="assets/jquery-ui-dist/jquery-ui.min.js"></script>
         <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script>
+        <script type="text/javascript" src="assets/moment/min/moment.min.js"></script>
         <script type="text/javascript" src="assets/qtip2/dist/jquery.qtip.min.js"></script>
         <script type="text/javascript" src="assets/slickgrid/lib/jquery.event.drag-2.3.0.js"></script>
         <script type="text/javascript" src="assets/slickgrid/plugins/slick.cellrangeselector.js"></script>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp
index 6efffdc..1204e19 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/login.jsp
@@ -40,6 +40,7 @@
         <script type="text/javascript" src="assets/jquery-ui-dist/jquery-ui.min.js"></script>
         <script type="text/javascript" src="js/nf/nf-namespace.js?${project.version}"></script>
         <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script>
+        <script type="text/javascript" src="assets/moment/min/moment.min.js"></script>
         ${nf.login.script.tags}
     </head>
     <body class="login-body">
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp
index 18fa0c9..9862a59 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/provenance.jsp
@@ -45,6 +45,7 @@
         <script type="text/javascript" src="js/jquery/jquery.each.js"></script>
         <script type="text/javascript" src="assets/jquery-ui-dist/jquery-ui.min.js"></script>
         <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script>
+        <script type="text/javascript" src="assets/moment/min/moment.min.js"></script>
         <script type="text/javascript" src="assets/qtip2/dist/jquery.qtip.min.js"></script>
         <script type="text/javascript" src="assets/slickgrid/lib/jquery.event.drag-2.3.0.js"></script>
         <script type="text/javascript" src="assets/slickgrid/plugins/slick.cellrangeselector.js"></script>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp
index 394f630..5d6ad09 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/summary.jsp
@@ -50,6 +50,7 @@
         <script type="text/javascript" src="js/jquery/jquery.ellipsis.js"></script>
         <script type="text/javascript" src="js/jquery/jquery.each.js"></script>
         <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script>
+        <script type="text/javascript" src="assets/moment/min/moment.min.js"></script>
         <script type="text/javascript" src="assets/qtip2/dist/jquery.qtip.min.js"></script>
         <script type="text/javascript" src="assets/slickgrid/lib/jquery.event.drag-2.3.0.js"></script>
         <script type="text/javascript" src="assets/slickgrid/plugins/slick.cellrangeselector.js"></script>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp
index f176d03..68b95d9 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/templates.jsp
@@ -41,6 +41,7 @@
         <script type="text/javascript" src="js/jquery/jquery.each.js"></script>
         <script type="text/javascript" src="assets/jquery-ui-dist/jquery-ui.min.js"></script>
         <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script>
+        <script type="text/javascript" src="assets/moment/min/moment.min.js"></script>
         <script type="text/javascript" src="assets/qtip2/dist/jquery.qtip.min.js"></script>
         <script type="text/javascript" src="assets/slickgrid/lib/jquery.event.drag-2.3.0.js"></script>
         <script type="text/javascript" src="assets/slickgrid/plugins/slick.cellrangeselector.js"></script>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp
index 667f2ef..4295fdf 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/users.jsp
@@ -43,6 +43,7 @@
         <script type="text/javascript" src="js/jquery/jquery.ellipsis.js"></script>
         <script type="text/javascript" src="js/jquery/jquery.each.js"></script>
         <script type="text/javascript" src="assets/lodash-core/distrib/lodash-core.min.js"></script>
+        <script type="text/javascript" src="assets/moment/min/moment.min.js"></script>
         <script type="text/javascript" src="assets/qtip2/dist/jquery.qtip.min.js"></script>
         <script type="text/javascript" src="assets/slickgrid/lib/jquery.event.drag-2.3.0.js"></script>
         <script type="text/javascript" src="assets/slickgrid/plugins/slick.cellrangeselector.js"></script>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/graph.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/graph.css
index 4c9f289..f47771b 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/graph.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/graph.css
@@ -344,7 +344,24 @@ text.connection-from-run-status.is-missing-port, text.connection-to-run-status.i
 }
 
 g.connection rect.backpressure-tick {
-    fill: #3e3e3e;
+    fill: transparent;
+}
+
+g.connection rect.backpressure-tick.data-size-prediction.prediction-down,
+g.connection rect.backpressure-tick.object-prediction.prediction-down {
+    fill: white;
+}
+
+g.connection rect.backpressure-tick.data-size-prediction,
+g.connection rect.backpressure-tick.object-prediction {
+    fill: black;
+}
+
+g.connection rect.backpressure-tick.data-size-prediction.not-configured,
+g.connection rect.backpressure-tick.object-prediction.not-configured,
+g.connection rect.backpressure-tick.data-size-prediction.prediction-down.not-configured,
+g.connection rect.backpressure-tick.object-prediction.prediction-down.not-configured {
+    fill: transparent;
 }
 
 g.connection rect.backpressure-tick.not-configured {
@@ -355,6 +372,12 @@ g.connection rect.backpressure-object, g.connection rect.backpressure-data-size
     fill: #d8d8d8;
 }
 
+/**/
+g.connection rect.backpressure-object>title>tspan, g.connection rect.backpressure-data-size>title>tspan {
+    display: block;
+}
+/**/
+
 g.connection rect.backpressure-object.not-configured, g.connection rect.backpressure-data-size.not-configured {
     fill: transparent;
 }
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js
index b325c32..2a50f4c 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-connection.js
@@ -26,9 +26,10 @@
                 'nf.Storage',
                 'nf.ErrorHandler',
                 'nf.Client',
-                'nf.CanvasUtils'],
-            function ($, d3, nfCommon, nfDialog, nfStorage, nfErrorHandler, nfClient, nfCanvasUtils) {
-                return (nf.Connection = factory($, d3, nfCommon, nfDialog, nfStorage, nfErrorHandler, nfClient, nfCanvasUtils));
+                'nf.CanvasUtils',
+                'lodash-core'],
+            function ($, d3, nfCommon, nfDialog, nfStorage, nfErrorHandler, nfClient, nfCanvasUtils, _) {
+                return (nf.Connection = factory($, d3, nfCommon, nfDialog, nfStorage, nfErrorHandler, nfClient, nfCanvasUtils, _));
             });
     } else if (typeof exports === 'object' && typeof module === 'object') {
         module.exports = (nf.Connection =
@@ -39,7 +40,8 @@
                 require('nf.Storage'),
                 require('nf.ErrorHandler'),
                 require('nf.Client'),
-                require('nf.CanvasUtils')));
+                require('nf.CanvasUtils'),
+                require('lodash-code')));
     } else {
         nf.Connection = factory(root.$,
             root.d3,
@@ -48,9 +50,10 @@
             root.nf.Storage,
             root.nf.ErrorHandler,
             root.nf.Client,
-            root.nf.CanvasUtils);
+            root.nf.CanvasUtils,
+            root._);
     }
-}(this, function ($, d3, nfCommon, nfDialog, nfStorage, nfErrorHandler, nfClient, nfCanvasUtils) {
+}(this, function ($, d3, nfCommon, nfDialog, nfStorage, nfErrorHandler, nfClient, nfCanvasUtils, _) {
     'use strict';
 
     var nfSelectable;
@@ -66,6 +69,9 @@
     // width of a backpressure indicator - half of width, left/right padding, left/right border
     var backpressureBarWidth = (dimensions.width / 2) - 15 - 2;
 
+    var backpressureCountOffset = 6;
+    var backpressureDataSizeOffset = (dimensions.width / 2) + 10 + 1;
+
     // --------------------------
     // Snap alignment for drag events
     // --------------------------
@@ -1238,92 +1244,121 @@
                         var yBackpressureOffset = rowHeight + HEIGHT_FOR_BACKPRESSURE - 4;
 
                         // backpressure object threshold
+                        var backpressureObjectContainer = queued.append('g')
+                            .attrs({
+                                'transform': 'translate(' + backpressureCountOffset + ', ' + yBackpressureOffset + ')',
+                                'class': 'backpressure-object-container'
+                            });
 
                         // start
-                        queued.append('rect')
+                        backpressureObjectContainer.append('rect')
                             .attrs({
                                 'class': 'backpressure-tick object',
                                 'width': 1,
                                 'height': 3,
-                                'x': 5,
-                                'y': yBackpressureOffset
+                                'x': 0,
+                                'y': 0
                             });
 
                         // bar
-                        var backpressureCountOffset = 6;
-                        queued.append('rect')
+                        backpressureObjectContainer.append('rect')
                             .attrs({
                                 'class': 'backpressure-object',
                                 'width': backpressureBarWidth,
                                 'height': 3,
-                                'x': backpressureCountOffset,
-                                'y': yBackpressureOffset
-                            })
-                            .append('title');
+                                'x': 0,
+                                'y': 0
+                            });
 
                         // end
-                        queued.append('rect')
+                        backpressureObjectContainer.append('rect')
                             .attrs({
                                 'class': 'backpressure-tick object',
                                 'width': 1,
                                 'height': 3,
-                                'x': backpressureCountOffset + backpressureBarWidth,
-                                'y': yBackpressureOffset
+                                'x': backpressureBarWidth,
+                                'y': 0
                             });
 
                         // percent full
-                        queued.append('rect')
+                        backpressureObjectContainer.append('rect')
                             .attrs({
                                 'class': 'backpressure-percent object',
                                 'width': 0,
                                 'height': 3,
-                                'x': backpressureCountOffset,
-                                'y': yBackpressureOffset
+                                'x': 0,
+                                'y': 0
+                            });
+
+                        // prediction indicator
+                        backpressureObjectContainer.append('rect')
+                            .attrs({
+                                'class': 'backpressure-tick object-prediction',
+                                'width': 1,
+                                'height': 3,
+                                'x': backpressureBarWidth,
+                                'y': 0
                             });
 
                         // backpressure data size threshold
 
+                        var backpressureDataSizeContainer = queued.append('g')
+                            .attrs({
+                                'transform': 'translate(' + backpressureDataSizeOffset + ', ' + yBackpressureOffset + ')',
+                                'class': 'backpressure-data-size-container'
+                            });
+
                         // start
-                        queued.append('rect')
+                        backpressureDataSizeContainer.append('rect')
                             .attrs({
                                 'class': 'backpressure-tick data-size',
                                 'width': 1,
                                 'height': 3,
-                                'x': (dimensions.width / 2) + 10,
-                                'y': yBackpressureOffset
+                                'x': 0,
+                                'y': 0
                             });
 
                         // bar
-                        var backpressureDataSizeOffset = (dimensions.width / 2) + 10 + 1;
-                        queued.append('rect')
+                        backpressureDataSizeContainer.append('rect')
                             .attrs({
                                 'class': 'backpressure-data-size',
                                 'width': backpressureBarWidth,
                                 'height': 3,
-                                'x': backpressureDataSizeOffset,
-                                'y': yBackpressureOffset
+                                'x': 0,
+                                'y': 0
                             })
                             .append('title');
 
                         // end
-                        queued.append('rect')
+                        backpressureDataSizeContainer.append('rect')
                             .attrs({
                                 'class': 'backpressure-tick data-size',
                                 'width': 1,
                                 'height': 3,
-                                'x': backpressureDataSizeOffset + backpressureBarWidth,
-                                'y': yBackpressureOffset
+                                'x': backpressureBarWidth,
+                                'y': 0
                             });
 
                         // percent full
-                        queued.append('rect')
+                        backpressureDataSizeContainer.append('rect')
                             .attrs({
                                 'class': 'backpressure-percent data-size',
                                 'width': 0,
                                 'height': 3,
-                                'x': backpressureDataSizeOffset,
-                                'y': yBackpressureOffset
+                                'x': 0,
+                                'y': 0
                             });
+
+                        // prediction indicator
+                        backpressureDataSizeContainer.append('rect')
+                            .attrs({
+                                'class': 'backpressure-tick data-size-prediction',
+                                'width': 1,
+                                'height': 3,
+                                'x': backpressureBarWidth,
+                                'y': 0
+                            });
+
                     } else {
                         backgrounds.push(queued.select('rect.connection-label-background'));
                         borders.push(queued.select('rect.connection-label-border'));
@@ -1441,6 +1476,10 @@
                         .classed('not-configured', function () {
                             return nfCommon.isUndefinedOrNull(d.status.aggregateSnapshot.percentUseCount);
                         });
+                    connectionLabelContainer.selectAll('rect.backpressure-tick.object-prediction')
+                        .classed('not-configured', function () {
+                            return nfCommon.isUndefinedOrNull(d.status.aggregateSnapshot.percentUseCount);
+                        });
 
                     // update backpressure data size fill
                     connectionLabelContainer.select('rect.backpressure-data-size')
@@ -1451,6 +1490,10 @@
                         .classed('not-configured', function () {
                             return nfCommon.isUndefinedOrNull(d.status.aggregateSnapshot.percentUseBytes);
                         });
+                    connectionLabelContainer.selectAll('rect.backpressure-tick.data-size-prediction')
+                        .classed('not-configured', function () {
+                            return nfCommon.isUndefinedOrNull(d.status.aggregateSnapshot.percentUseBytes);
+                        });
 
                     if (d.permissions.canWrite) {
                         // only support dragging the label when appropriate
@@ -1476,6 +1519,95 @@
         });
     };
 
+    var isAtBackPressure = function (d) {
+        var percentUseCount = _.get(d, 'status.aggregateSnapshot.percentUseCount', 0);
+        var percentUseBytes = _.get(d, 'status.aggregateSnapshot.percentUseBytes', 0);
+        return Math.max(percentUseCount, percentUseBytes) >= 100;
+    };
+
+    /**
+     * Gets the tooltip content for the back pressure count metric
+     * @param d
+     */
+    var getBackPressureCountTip = function (d) {
+        var tooltipContent;
+        var percentUseCount = _.get(d, 'status.aggregateSnapshot.percentUseCount');
+        if (_.isNumber(percentUseCount)) {
+            var objectThreshold = _.get(d, 'component.backPressureObjectThreshold');
+
+            var predictedPercentCount = _.get(d, 'status.aggregateSnapshot.predictedPercentCount', -1);
+            var timeToBackPressure = _.get(d, 'status.aggregateSnapshot.predictedMillisUntilCountBackpressure', -1);
+
+            var tooltipLines = ['Queue: ' + _.clamp(percentUseCount, 0, 100) + '% full (based on ' + objectThreshold + ' object threshold)'];
+
+            // only show predicted percent if it is non-negative
+            if (_.isNumber(predictedPercentCount) && predictedPercentCount > -1) {
+                var predictionIntervalSeconds = _.get(d, 'status.aggregateSnapshot.predictionIntervalSeconds', 60 * 5);
+                tooltipLines.push('Predicted queue (next ' + (predictionIntervalSeconds / 60 ) + ' mins): ' + _.clamp(predictedPercentCount, 0, 100) + '%')
+            }
+
+            // only show an estimate if it is valid (non-negative but less than the max number supported)
+            if (_.isNumber(timeToBackPressure) && _.inRange(timeToBackPressure, 0, Number.MAX_SAFE_INTEGER) && !isAtBackPressure(d)) {
+                var duration = nfCommon.formatPredictedDuration(timeToBackPressure);
+                tooltipLines.push('Estimated time to back pressure: ' + duration);
+            }
+
+            if (_.isEmpty(tooltipLines)) {
+                return '';
+            } else if (_.size(tooltipLines) === 1) {
+                return tooltipLines[0];
+            } else {
+                tooltipContent = nfCommon.formatUnorderedList(tooltipLines)
+            }
+        } else {
+            tooltipContent = 'Back Pressure Object Threshold is not configured';
+        }
+
+        return tooltipContent;
+    };
+
+    /**
+     * Gets the tooltip content for the back pressure size metric
+     * @param d
+     */
+    var getBackPressureSizeTip = function (d) {
+        var tooltipContent;
+        var percentUseBytes = _.get(d, 'status.aggregateSnapshot.percentUseBytes');
+
+        if (_.isNumber(percentUseBytes)) {
+            var dataSizeThreshold = _.get(d, 'component.backPressureDataSizeThreshold');
+
+            var predictedPercentBytes = _.get(d, 'status.aggregateSnapshot.predictedPercentBytes', -1);
+            var timeToBackPressure = _.get(d, 'status.aggregateSnapshot.predictedMillisUntilBytesBackpressure', -1);
+
+            var tooltipLines = ['Queue: ' + _.clamp(percentUseBytes, 0, 100) + '% full (based on ' + dataSizeThreshold + ' data size threshold)'];
+
+            // only show predicted percent if it is non-negative
+            if (_.isNumber(predictedPercentBytes) && predictedPercentBytes > -1) {
+                var predictionIntervalSeconds = _.get(d, 'status.aggregateSnapshot.predictionIntervalSeconds', 60 * 5);
+                tooltipLines.push('Predicted queue (next ' + (predictionIntervalSeconds / 60 ) + ' mins): ' + _.clamp(predictedPercentBytes, 0, 100) + '%')
+            }
+
+            // only show an estimate if it is valid (non-negative but less than the max number supported)
+            if (_.isNumber(timeToBackPressure) && _.inRange(timeToBackPressure, 0, Number.MAX_SAFE_INTEGER) && !isAtBackPressure(d)) {
+                var duration = nfCommon.formatPredictedDuration(timeToBackPressure);
+                tooltipLines.push('Estimated time to back pressure: ' + duration);
+            }
+
+            if (_.isEmpty(tooltipLines)) {
+                return '';
+            } else if (_.size(tooltipLines) === 1) {
+                return tooltipLines[0];
+            } else {
+                tooltipContent = nfCommon.formatUnorderedList(tooltipLines)
+            }
+        } else {
+            tooltipContent = 'Back Pressure Data Size Threshold is not configured';
+        }
+
+        return tooltipContent;
+    };
+
     /**
      * Updates the stats of the connections in the specified selection.
      *
@@ -1517,13 +1649,53 @@
                 deferred.resolve();
             });
 
-            updated.select('rect.backpressure-data-size').select('title').text(function (d) {
-                if (nfCommon.isDefinedAndNotNull(d.status.aggregateSnapshot.percentUseBytes)) {
-                    return 'Queue is ' + d.status.aggregateSnapshot.percentUseBytes + '% full based on Back Pressure Data Size Threshold';
-                } else {
-                    return 'Back Pressure Data Size Threshold is not configured';
-                }
+            var backpressurePercentDataSizePrediction = updated.select('rect.backpressure-tick.data-size-prediction');
+            backpressurePercentDataSizePrediction.transition()
+                .duration(400)
+                .attrs({
+                    'x': function (d) {
+                        // clamp the prediction between 0 and 100 percent
+                        var predicted = _.get(d, 'status.aggregateSnapshot.predictedPercentBytes', 0);
+                        return (backpressureBarWidth * _.clamp(predicted, 0, 100)) / 100;
+                    },
+                    'display': function (d) {
+                        var predicted = _.get(d, 'status.aggregateSnapshot.predictedPercentBytes', -1);
+                        if (predicted >= 0) {
+                            return 'unset';
+                        } else {
+                            // don't show it if there not a valid prediction
+                            return 'none';
+                        }
+                    }
+                }).on('end', function () {
+                    backpressurePercentDataSizePrediction.classed('prediction-down', function (d) {
+                        var actual = _.get(d, 'status.aggregateSnapshot.percentUseBytes', 0);
+                        var predicted = _.get(d, 'status.aggregateSnapshot.predictedPercentBytes', 0);
+                        return predicted < actual;
+                })
             });
+
+            updated.select('g.backpressure-data-size-container')
+                .each(function(d) {
+                    var tip = d3.select('#back-pressure-size-tip-' + d.id);
+
+                    // create a DOM element for the tooltip if ones does not already exist
+                    if (tip.empty()) {
+                        tip = d3.select('#connection-tooltips')
+                            .append('div')
+                            .attr('id', function() {
+                                return 'back-pressure-size-tip-' + d.id
+                            })
+                            .attr('class', 'tooltip nifi-tooltip');
+                    }
+
+                    // update the tooltip
+                    tip.html(function() {
+                        return $('<div></div>').append(getBackPressureSizeTip(d)).html();
+                    });
+
+                    nfCanvasUtils.canvasTooltip(tip, d3.select(this));
+                });
         }).promise();
 
         // update object count
@@ -1546,24 +1718,65 @@
                         }
                     }
                 }).on('end', function () {
-                backpressurePercentObject
-                    .classed('warning', function (d) {
-                        return isWarningCount(d);
+                    backpressurePercentObject
+                        .classed('warning', function (d) {
+                            return isWarningCount(d);
+                        })
+                        .classed('error', function (d) {
+                            return isErrorCount(d);
+                        });
+
+                    deferred.resolve();
+                });
+
+
+            var backpressurePercentObjectPrediction = updated.select('rect.backpressure-tick.object-prediction');
+            backpressurePercentObjectPrediction.transition()
+                .duration(400)
+                .attrs({
+                    'x': function (d) {
+                        // clamp the prediction between 0 and 100 percent
+                        var predicted = _.get(d, 'status.aggregateSnapshot.predictedPercentCount', 0);
+                        return (backpressureBarWidth * _.clamp(predicted, 0, 100)) / 100;
+                    },
+                    'display': function (d) {
+                        var predicted = _.get(d, 'status.aggregateSnapshot.predictedPercentCount', -1);
+                        if (predicted >= 0) {
+                            return 'unset';
+                        } else {
+                            // don't show it if there not a valid prediction
+                            return 'none';
+                        }
+                    }
+                }).on('end', function () {
+                    backpressurePercentObjectPrediction.classed('prediction-down', function (d) {
+                        var actual = _.get(d, 'status.aggregateSnapshot.percentUseCount', 0);
+                        var predicted = _.get(d, 'status.aggregateSnapshot.predictedPercentCount', 0);
+                        return predicted < actual;
                     })
-                    .classed('error', function (d) {
-                        return isErrorCount(d);
-                    });
+                });
 
-                deferred.resolve();
-            });
+            updated.select('g.backpressure-object-container')
+                .each(function(d) {
+                    var tip = d3.select('#back-pressure-count-tip-' + d.id);
 
-            updated.select('rect.backpressure-object').select('title').text(function (d) {
-                if (nfCommon.isDefinedAndNotNull(d.status.aggregateSnapshot.percentUseCount)) {
-                    return 'Queue is ' + d.status.aggregateSnapshot.percentUseCount + '% full based on Back Pressure Object Threshold';
-                } else {
-                    return 'Back Pressure Object Threshold is not configured';
-                }
-            });
+                    // create a DOM element for the tooltip if ones does not already exist
+                    if (tip.empty()) {
+                        tip = d3.select('#connection-tooltips')
+                            .append('div')
+                            .attr('id', function() {
+                                return 'back-pressure-count-tip-' + d.id
+                            })
+                            .attr('class', 'tooltip nifi-tooltip');
+                    }
+
+                    // update the tooltip
+                    tip.html(function() {
+                        return $('<div></div>').append(getBackPressureCountTip(d)).html();
+                    });
+
+                    nfCanvasUtils.canvasTooltip(tip, d3.select(this));
+                });
         }).promise();
 
         // update connection once progress bars have transitioned
@@ -1627,7 +1840,15 @@
         });
 
         // remove the connection
-        removed.remove();
+        removed.call(removeTooltips).remove();
+    };
+
+    var removeTooltips = function (removed) {
+        removed.each(function (d) {
+            // remove any associated tooltips
+            $('#back-pressure-size-tip-' + d.id).remove();
+            $('#back-pressure-count-tip-' + d.id).remove();
+        });
     };
 
     var nfConnection = {
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
index 7e5a1c0..13ee565 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
@@ -23,22 +23,25 @@
         define(['jquery',
                 'd3',
                 'nf.Storage',
-                'lodash-core'],
-            function ($, d3, nfStorage, _) {
-                return (nf.Common = factory($, d3, nfStorage, _));
+                'lodash-core',
+                'moment'],
+            function ($, d3, nfStorage, _, moment) {
+                return (nf.Common = factory($, d3, nfStorage, _, moment));
             });
     } else if (typeof exports === 'object' && typeof module === 'object') {
         module.exports = (nf.Common = factory(require('jquery'),
             require('d3'),
             require('nf.Storage'),
-            require('lodash-core')));
+            require('lodash-core'),
+            require('moment')));
     } else {
         nf.Common = factory(root.$,
             root.d3,
             root.nf.Storage,
-            root._);
+            root._,
+            root.moment);
     }
-}(this, function ($, d3, nfStorage, _) {
+}(this, function ($, d3, nfStorage, _, moment) {
     'use strict';
 
     $(document).ready(function () {
@@ -1278,6 +1281,13 @@
             }
         },
 
+        formatPredictedDuration: function (duration) {
+            if (duration === 0) {
+                return 'now';
+            }
+            return moment.duration(duration, 'ms').humanize();
+        },
+
         /**
          * Constants for formatting data size.
          */
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js
index 3f2fd1d..4f02e74 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/summary/nf-summary-table.js
@@ -25,9 +25,10 @@
                 'nf.StatusHistory',
                 'nf.ProcessorDetails',
                 'nf.ConnectionDetails',
-                'nf.ng.Bridge'],
-            function ($, Slick, nfCommon, nfErrorHandler, nfStatusHistory, nfProcessorDetails, nfConnectionDetails, nfNgBridge) {
-                return (nf.SummaryTable = factory($, Slick, nfCommon, nfErrorHandler, nfStatusHistory, nfProcessorDetails, nfConnectionDetails, nfNgBridge));
+                'nf.ng.Bridge',
+                'lodash-core'],
+            function ($, Slick, nfCommon, nfErrorHandler, nfStatusHistory, nfProcessorDetails, nfConnectionDetails, nfNgBridge, _) {
+                return (nf.SummaryTable = factory($, Slick, nfCommon, nfErrorHandler, nfStatusHistory, nfProcessorDetails, nfConnectionDetails, nfNgBridge, _));
             });
     } else if (typeof exports === 'object' && typeof module === 'object') {
         module.exports = (nf.SummaryTable =
@@ -38,7 +39,8 @@
                 require('nf.StatusHistory'),
                 require('nf.ProcessorDetails'),
                 require('nf.ConnectionDetails'),
-                require('nf.ng.Bridge')));
+                require('nf.ng.Bridge'),
+                require('lodash-core')));
     } else {
         nf.SummaryTable = factory(root.$,
             root.Slick,
@@ -47,9 +49,10 @@
             root.nf.StatusHistory,
             root.nf.ProcessorDetails,
             root.nf.ConnectionDetails,
-            root.nf.ng.Bridge);
+            root.nf.ng.Bridge,
+            root._);
     }
-}(this, function ($, Slick, nfCommon, nfErrorHandler, nfStatusHistory, nfProcessorDetails, nfConnectionDetails, nfNgBridge) {
+}(this, function ($, Slick, nfCommon, nfErrorHandler, nfStatusHistory, nfProcessorDetails, nfConnectionDetails, nfNgBridge, _) {
     'use strict';
 
     /**
@@ -64,6 +67,8 @@
         }
     };
 
+    var DATA_SEPARATOR = '&nbsp;&nbsp;|&nbsp;&nbsp;';
+
     /**
      * Goes to the specified component if possible.
      *
@@ -291,12 +296,12 @@
 
         // formatter for io
         var ioFormatter = function (row, cell, value, columnDef, dataContext) {
-            return nfCommon.escapeHtml(dataContext.read) + ' / ' + nfCommon.escapeHtml(dataContext.written);
+            return nfCommon.escapeHtml(dataContext.read) + DATA_SEPARATOR + nfCommon.escapeHtml(dataContext.written);
         };
 
         // formatter for tasks
         var taskTimeFormatter = function (row, cell, value, columnDef, dataContext) {
-            return nfCommon.formatInteger(dataContext.tasks) + ' / ' + nfCommon.escapeHtml(dataContext.tasksDuration);
+            return nfCommon.formatInteger(dataContext.tasks) + DATA_SEPARATOR + nfCommon.escapeHtml(dataContext.tasksDuration);
         };
 
         // function for formatting the last accessed time
@@ -309,7 +314,7 @@
             var threadCounts = '';
             var threadTip = '';
             if (dataContext.terminatedThreadCount > 0) {
-                threadCounts = '(' + dataContext.activeThreadCount + ' / ' + dataContext.terminatedThreadCount + ')';
+                threadCounts = '(' + dataContext.activeThreadCount + DATA_SEPARATOR + dataContext.terminatedThreadCount + ')';
                 threadTip = 'Threads: (Active / Terminated)';
             } else if (dataContext.activeThreadCount > 0) {
                 threadCounts = '(' + dataContext.activeThreadCount + ')';
@@ -372,7 +377,7 @@
         var inputColumn = {
             id: 'input',
             field: 'input',
-            name: '<span class="input-title">In</span>&nbsp;/&nbsp;<span class="input-size-title">Size</span>&nbsp;<span style="font-weight: normal; overflow: hidden;">5 min</span>',
+            name: '<span class="input-title">In</span>&nbsp;(<span class="input-size-title">Size</span>)&nbsp;<span style="font-weight: normal; overflow: hidden;">5 min</span>',
             toolTip: 'Count / data size in the last 5 min',
             sortable: true,
             defaultSortAsc: false,
@@ -382,7 +387,7 @@
         var ioColumn = {
             id: 'io',
             field: 'io',
-            name: '<span class="read-title">Read</span>&nbsp;/&nbsp;<span class="written-title">Write</span>&nbsp;<span style="font-weight: normal; overflow: hidden;">5 min</span>',
+            name: '<span class="read-title">Read</span>' + DATA_SEPARATOR + '<span class="written-title">Write</span>&nbsp;<span style="font-weight: normal; overflow: hidden;">5 min</span>',
             toolTip: 'Data size in the last 5 min',
             formatter: ioFormatter,
             sortable: true,
@@ -392,7 +397,7 @@
         var outputColumn = {
             id: 'output',
             field: 'output',
-            name: '<span class="output-title">Out</span>&nbsp;/&nbsp;<span class="output-size-title">Size</span>&nbsp;<span style="font-weight: normal; overflow: hidden;">5 min</span>',
+            name: '<span class="output-title">Out</span>&nbsp;(<span class="output-size-title">Size</span>)&nbsp;<span style="font-weight: normal; overflow: hidden;">5 min</span>',
             toolTip: 'Count / data size in the last 5 min',
             sortable: true,
             defaultSortAsc: false,
@@ -402,7 +407,7 @@
         var tasksTimeColumn = {
             id: 'tasks',
             field: 'tasks',
-            name: '<span class="tasks-title">Tasks</span>&nbsp;/&nbsp;<span class="time-title">Time</span>&nbsp;<span style="font-weight: normal; overflow: hidden;">5 min</span>',
+            name: '<span class="tasks-title">Tasks</span>' + DATA_SEPARATOR + '<span class="time-title">Time</span>&nbsp;<span style="font-weight: normal; overflow: hidden;">5 min</span>',
             toolTip: 'Count / duration in the last 5 min',
             formatter: taskTimeFormatter,
             sortable: true,
@@ -708,23 +713,58 @@
             return '<div class="pointer show-connection-details fa fa-info-circle" title="View Connection Details"></div>';
         };
 
+        var formatPercent = function (value) {
+            return _.isNumber(value) && value >= 0 ? _.clamp(value, 0, 100) + '%' : 'NA';
+        };
+
         var backpressureFormatter = function (row, cell, value, columnDef, dataContext) {
             var percentUseCount = 'NA';
             if (nfCommon.isDefinedAndNotNull(dataContext.percentUseCount)) {
-                percentUseCount = dataContext.percentUseCount + '%';
+                percentUseCount = formatPercent(dataContext.percentUseCount);
             }
             var percentUseBytes = 'NA';
             if (nfCommon.isDefinedAndNotNull(dataContext.percentUseBytes)) {
-                percentUseBytes = dataContext.percentUseBytes + '%';
+                percentUseBytes = formatPercent(dataContext.percentUseBytes);
+            }
+            return nfCommon.escapeHtml(percentUseCount) + DATA_SEPARATOR + nfCommon.escapeHtml(percentUseBytes);
+        };
+
+        var backpressurePredictionFormatter = function (row, cell, value, columnDef, dataContext) {
+            var predictedMillisUntilBytesBackpressure = _.get(dataContext, 'predictedMillisUntilBytesBackpressure', -1);
+            var predictedMillisUntilCountBackpressure = _.get(dataContext, 'predictedMillisUntilCountBackpressure', -1);
+
+            var percentUseCount = _.get(dataContext, 'percentUseCount', 0);
+            var percentUseBytes = _.get(dataContext, 'percentUseBytes', 0);
+
+            var predictions = [
+                { label: 'object', timeToBackPressure: predictedMillisUntilCountBackpressure },
+                { label: 'size', timeToBackPressure: predictedMillisUntilBytesBackpressure },
+            ];
+            var actualQueuePercents = [
+                { label: 'object', percent: percentUseCount },
+                { label: 'size', percent: percentUseBytes }
+            ];
+
+            var minPrediction = _.minBy(predictions, 'timeToBackPressure');
+            var maxActual = _.maxBy(actualQueuePercents, 'percent');
+
+            if (maxActual.percent >= 100) {
+                // currently experiencing back pressure
+                return 'now (' + maxActual.label + ')';
+            } else if (minPrediction.timeToBackPressure < 0) {
+                // there is not a valid time-to-back-pressure prediction available
+                return 'NA';
             }
-            return nfCommon.escapeHtml(percentUseCount) + ' / ' + nfCommon.escapeHtml(percentUseBytes);
+
+            var formatted = nfCommon.formatPredictedDuration(minPrediction.timeToBackPressure);
+            return nfCommon.escapeHtml(formatted) + ' (' + minPrediction.label + ')';
         };
 
         // define the input, read, written, and output columns (reused between both tables)
         var queueColumn = {
             id: 'queued',
             field: 'queued',
-            name: '<span class="queued-title">Queue</span>&nbsp;/&nbsp;<span class="queued-size-title">Size</span>',
+            name: '<span class="queued-title">Queue</span>&nbsp;(<span class="queued-size-title">Size</span>)',
             sortable: true,
             defaultSortAsc: false,
             resize: true,
@@ -735,13 +775,25 @@
         var backpressureColumn = {
             id: 'backpressure',
             field: 'backpressure',
-            name: '<span class="backpressure-object-title">Queue</span>&nbsp;/&nbsp;<span class="backpressure-data-size-title">Size</span> Threshold',
+            name: 'Threshold %: <span class="backpressure-object-title">Queue</span>&nbsp;&nbsp;|&nbsp;&nbsp;<span class="backpressure-data-size-title">Size</span>',
             sortable: true,
             defaultSortAsc: false,
             formatter: backpressureFormatter,
             resize: true
         };
 
+        // define the column used to display backpressure predicted values (reused in both tables)
+        var backpressurePredictionColumn = {
+            id: 'backpressurePrediction',
+            field: 'backpressurePrediction',
+            name: 'Estimated Time to Back Pressure',
+            sortable: true,
+            defaultSortAsc: false,
+            formatter: backpressurePredictionFormatter,
+            resize: true,
+            toolTip: 'Estimated Time to Back Pressure'
+        };
+
         // define the column model for the summary table
         var connectionsColumnModel = [
             {
@@ -754,14 +806,6 @@
                 maxWidth: 50
             },
             {
-                id: 'sourceName',
-                field: 'sourceName',
-                name: 'Source Name',
-                sortable: true,
-                resizable: true,
-                formatter: nfCommon.genericValueFormatter
-            },
-            {
                 id: 'name',
                 field: 'name',
                 name: 'Name',
@@ -769,18 +813,27 @@
                 resizable: true,
                 formatter: valueFormatter
             },
+            queueColumn,
+            backpressureColumn,
+            backpressurePredictionColumn,
+            inputColumn,
+            {
+                id: 'sourceName',
+                field: 'sourceName',
+                name: 'From Source',
+                sortable: true,
+                resizable: true,
+                formatter: nfCommon.genericValueFormatter
+            },
+            outputColumn,
             {
                 id: 'destinationName',
                 field: 'destinationName',
-                name: 'Destination Name',
+                name: 'To Destination',
                 sortable: true,
                 resizable: true,
                 formatter: nfCommon.genericValueFormatter
-            },
-            inputColumn,
-            queueColumn,
-            backpressureColumn,
-            outputColumn
+            }
         ];
 
         // add an action column if appropriate
@@ -952,9 +1005,10 @@
                 resizable: true,
                 formatter: nfCommon.genericValueFormatter
             },
-            inputColumn,
             queueColumn,
             backpressureColumn,
+            backpressurePredictionColumn,
+            inputColumn,
             outputColumn
         ];
 
@@ -1032,7 +1086,7 @@
         var transferredColumn = {
             id: 'transferred',
             field: 'transferred',
-            name: '<span class="transferred-title">Transferred</span>&nbsp;/&nbsp;<span class="transferred-size-title">Size</span>&nbsp;<span style="font-weight: normal; overflow: hidden;">5 min</span>',
+            name: '<span class="transferred-title">Transferred</span>&nbsp;(<span class="transferred-size-title">Size</span>)&nbsp;<span style="font-weight: normal; overflow: hidden;">5 min</span>',
             toolTip: 'Count / data size transferred to and from connections in the last 5 min',
             resizable: true,
             defaultSortAsc: false,
@@ -1042,7 +1096,7 @@
         var sentColumn = {
             id: 'sent',
             field: 'sent',
-            name: '<span class="sent-title">Sent</span>&nbsp;/&nbsp;<span class="sent-size-title">Size</span>&nbsp;<span style="font-weight: normal; overflow: hidden;">5 min</span>',
+            name: '<span class="sent-title">Sent</span>&nbsp;(<span class="sent-size-title">Size</span>)&nbsp;<span style="font-weight: normal; overflow: hidden;">5 min</span>',
             toolTip: 'Count / data size in the last 5 min',
             sortable: true,
             defaultSortAsc: false,
@@ -1052,7 +1106,7 @@
         var receivedColumn = {
             id: 'received',
             field: 'received',
-            name: '<span class="received-title">Received</span>&nbsp;/&nbsp;<span class="received-size-title">Size</span>&nbsp;<span style="font-weight: normal; overflow: hidden;">5 min</span>',
+            name: '<span class="received-title">Received</span>&nbsp;(<span class="received-size-title">Size</span>)&nbsp;<span style="font-weight: normal; overflow: hidden;">5 min</span>',
             toolTip: 'Count / data size in the last 5 min',
             sortable: true,
             defaultSortAsc: false,
@@ -2296,6 +2350,10 @@
                     var bPercentUseDataSize = nfCommon.isDefinedAndNotNull(b['percentUseBytes']) ? b['percentUseBytes'] : -1;
                     return aPercentUseDataSize - bPercentUseDataSize;
                 }
+            } else if (sortDetails.columnId === 'backpressurePrediction') {
+                var aMinTime = Math.min(_.get(a, 'predictedMillisUntilBytesBackpressure', Number.MAX_VALUE), _.get(a, 'predictedMillisUntilCountBackpressure', Number.MAX_VALUE));
+                var bMinTime = Math.min(_.get(b, 'predictedMillisUntilBytesBackpressure', Number.MAX_VALUE), _.get(b, 'predictedMillisUntilCountBackpressure', Number.MAX_VALUE));
+                return aMinTime - bMinTime;
             } else if (sortDetails.columnId === 'sent' || sortDetails.columnId === 'received' || sortDetails.columnId === 'input' || sortDetails.columnId === 'output' || sortDetails.columnId === 'transferred') {
                 var aSplit = a[sortDetails.columnId].split(/\(([^)]+)\)/);
                 var bSplit = b[sortDetails.columnId].split(/\(([^)]+)\)/);
@@ -2349,6 +2407,9 @@
         $('#' + tableId + ' span.queued-size-title').removeClass('sorted');
         $('#' + tableId + ' span.backpressure-object-title').removeClass('sorted');
         $('#' + tableId + ' span.backpressure-data-size-title').removeClass('sorted');
+        $('#' + tableId + ' span.backpressure-prediction-object-title').removeClass('sorted');
+        $('#' + tableId + ' span.backpressure-prediction-data-size-title').removeClass('sorted');
+        $('#' + tableId + ' span.backpressure-prediction-time-title').removeClass('sorted');
         $('#' + tableId + ' span.input-title').removeClass('sorted');
         $('#' + tableId + ' span.input-size-title').removeClass('sorted');
         $('#' + tableId + ' span.output-title').removeClass('sorted');
@@ -2776,6 +2837,13 @@
                         queuedSize: snapshot.queuedSize,
                         percentUseCount: snapshot.percentUseCount,
                         percentUseBytes: snapshot.percentUseBytes,
+                        predictedPercentBytes: snapshot.predictedPercentBytes,
+                        predictedPercentCount: snapshot.predictedPercentCount,
+                        predictedBytesAtNextInterval: snapshot.predictedBytesAtNextInterval,
+                        predictedCountAtNextInterval: snapshot.predictedCountAtNextInterval,
+                        predictedMillisUntilBytesBackpressure: snapshot.predictedMillisUntilBytesBackpressure,
+                        predictedMillisUntilCountBackpressure: snapshot.predictedMillisUntilCountBackpressure,
+                        predictionIntervalSeconds: snapshot.predictionIntervalSeconds,
                         output: snapshot.output
                     });
                 });