Repository: dask/distributed

Issue:
Chained tasks prevent getting intermediate results right after they're ready
Dask 0.15.4
Distributed 0.19.3

Consider this code:

```python
import logging
import time
from itertools import chain
from typing import List

from dask import delayed
from distributed import LocalCluster, Client, Future, as_completed
from distributed.diagnostics.plugin import SchedulerPlugin

logging.basicConfig(format='[%(asctime)s] %(message)s', level=logging.DEBUG)


class Plugin(SchedulerPlugin):
    def transition(self, key, start, finish, *args, **kwargs):
        super().transition(key, start, finish, *args, **kwargs)
        if finish == 'processing':
            logging.info('Started %s', key)
        elif finish == 'memory':
            logging.info('Finished %s', key)


dt = 3


@delayed(pure=True)
def task_a(x):
    time.sleep(dt)
    return x, 'A'


@delayed(pure=True)
def task_b(x):
    time.sleep(dt)
    return x[0], 'B'


@delayed(pure=True)
def task_c(x):
    time.sleep(dt)
    return x[0], 'C'


@delayed(pure=True)
def task_d(x):
    time.sleep(dt)
    return x[0], 'D'


def _main():
    task_a_result = [task_a(i, dask_key_name='task_a_{}'.format(i)) for i in range(10)]
    task_b_result = [task_b(a, dask_key_name='task_b_{}'.format(i)) for i, a in enumerate(task_a_result)]
    task_c_result = [task_c(b, dask_key_name='task_c_{}'.format(i)) for i, b in enumerate(task_b_result)]
    task_d_result = [task_d(c, dask_key_name='task_d_{}'.format(i)) for i, c in enumerate(task_c_result)]

    cluster = LocalCluster()
    logging.info('Cluster created with n_workers %s, threads_per_worker %s',
                 cluster.n_workers, cluster.threads_per_worker)
    cluster.scheduler.add_plugin(Plugin())
    client = Client(cluster)

    logging.info('Submitting')
    futures: List[Future] = client.compute(list(chain(task_a_result,
                                                      task_b_result,
                                                      task_c_result,
                                                      task_d_result)))

    for i, f in enumerate(futures):
        def mk_completed(idx):
            def completed(ft: Future):
                logging.info('## CB ## %s %s', idx, ft.result())

            return completed

        f.add_done_callback(mk_completed(i))

    for f in as_completed(futures):
        logging.info('## AS ## %s', f.result())


if __name__ == '__main__':
    _main()
```

It can provide an output similar to this one:
<details><summary>Click to expand</summary>
<p>

```
[2017-11-17 15:56:44,805] Cluster created with n_workers 8, threads_per_worker 1
[2017-11-17 15:56:44,810] Submitting
[2017-11-17 15:56:44,817] Started task_a_9
[2017-11-17 15:56:44,818] Started task_a_1
[2017-11-17 15:56:44,818] Started task_a_4
[2017-11-17 15:56:44,818] Started task_a_0
[2017-11-17 15:56:44,818] Started task_a_2
[2017-11-17 15:56:44,819] Started task_a_3
[2017-11-17 15:56:44,819] Started task_a_6
[2017-11-17 15:56:44,819] Started task_a_8
[2017-11-17 15:56:44,819] Started task_a_7
[2017-11-17 15:56:44,819] Started task_a_5
[2017-11-17 15:56:47,824] Finished task_a_7
[2017-11-17 15:56:47,824] Started task_b_7
[2017-11-17 15:56:47,824] Started finalize-dbdba2740eacf15e342f8f1772876e3e
[2017-11-17 15:56:47,829] Finished task_a_5
[2017-11-17 15:56:47,829] Started task_b_5
[2017-11-17 15:56:47,829] Started finalize-cd366bd4ef7ae54971e1239001beb7d7
[2017-11-17 15:56:47,830] Finished task_a_4
[2017-11-17 15:56:47,830] Started task_b_4
[2017-11-17 15:56:47,830] Started finalize-9323b45e08d18ce79ef5b9e1e76b52ec
[2017-11-17 15:56:47,831] Finished task_a_2
[2017-11-17 15:56:47,831] Started task_b_2
[2017-11-17 15:56:47,832] Started finalize-d65266bdeb7a5b95e852051afba2d9c8
[2017-11-17 15:56:47,832] Finished task_a_3
[2017-11-17 15:56:47,832] Started task_b_3
[2017-11-17 15:56:47,832] Started finalize-2101f21f864145fa2895063ebc606986
[2017-11-17 15:56:47,832] Finished task_a_8
[2017-11-17 15:56:47,832] Started task_b_8
[2017-11-17 15:56:47,832] Started finalize-7b7e859f4a6b2954b89462dd1f4eb7ae
[2017-11-17 15:56:47,833] Finished task_a_0
[2017-11-17 15:56:47,833] Started task_b_0
[2017-11-17 15:56:47,833] Started finalize-7881dcf3b60fbfbdc30e221cb857daf9
[2017-11-17 15:56:47,834] Finished task_a_6
[2017-11-17 15:56:47,834] Started task_b_6
[2017-11-17 15:56:47,835] Started finalize-c0e8082a1bc8ea41012e67242996cdb0
[2017-11-17 15:56:50,828] Finished task_a_9
[2017-11-17 15:56:50,828] Started task_b_9
[2017-11-17 15:56:50,828] Started finalize-db5bb4ece0ef14efc0d45702d77866dc
[2017-11-17 15:56:50,833] Finished task_a_1
[2017-11-17 15:56:50,833] Started task_b_1
[2017-11-17 15:56:50,833] Started finalize-bbe837a7bbf44618969603228d694f74
[2017-11-17 15:56:50,835] Finished task_b_4
[2017-11-17 15:56:50,835] Started task_c_4
[2017-11-17 15:56:50,835] Started finalize-d08efed1de50142f40fe9fea72076592
[2017-11-17 15:56:50,836] Finished task_b_0
[2017-11-17 15:56:50,836] Started task_c_0
[2017-11-17 15:56:50,837] Started finalize-2e3d005089e6c6217a02872a432e889f
[2017-11-17 15:56:50,837] Finished task_b_8
[2017-11-17 15:56:50,838] Started task_c_8
[2017-11-17 15:56:50,838] Started finalize-51321a3e536dec5cdf3c65536251d7d0
[2017-11-17 15:56:50,839] Finished finalize-9323b45e08d18ce79ef5b9e1e76b52ec
[2017-11-17 15:56:50,840] Finished task_b_3
[2017-11-17 15:56:50,840] Started task_c_3
[2017-11-17 15:56:50,840] Started finalize-a1706f943ad889e73505df69b35777dd
[2017-11-17 15:56:50,840] Finished task_b_2
[2017-11-17 15:56:50,841] Started task_c_2
[2017-11-17 15:56:50,841] Started finalize-3e143f8ddde23635b49f3c87f3f450db
[2017-11-17 15:56:50,842] Finished finalize-d65266bdeb7a5b95e852051afba2d9c8
[2017-11-17 15:56:50,847] Finished finalize-7b7e859f4a6b2954b89462dd1f4eb7ae
[2017-11-17 15:56:50,847] Finished finalize-7881dcf3b60fbfbdc30e221cb857daf9
[2017-11-17 15:56:50,849] Finished finalize-2101f21f864145fa2895063ebc606986
[2017-11-17 15:56:50,850] Finished task_b_6
[2017-11-17 15:56:50,850] Started task_c_6
[2017-11-17 15:56:50,850] Started finalize-be02238df1a8cfed547aeecb9c523684
[2017-11-17 15:56:50,850] Finished finalize-c0e8082a1bc8ea41012e67242996cdb0
[2017-11-17 15:56:50,857] ## CB ## 4 (4, 'A')
[2017-11-17 15:56:50,860] ## AS ## (4, 'A')
[2017-11-17 15:56:50,865] ## CB ## 2 (2, 'A')
[2017-11-17 15:56:50,868] ## AS ## (2, 'A')
[2017-11-17 15:56:50,874] ## CB ## 8 (8, 'A')
[2017-11-17 15:56:50,877] ## AS ## (8, 'A')
[2017-11-17 15:56:50,881] ## CB ## 0 (0, 'A')
[2017-11-17 15:56:50,883] ## AS ## (0, 'A')
[2017-11-17 15:56:50,889] ## CB ## 3 (3, 'A')
[2017-11-17 15:56:50,891] ## AS ## (3, 'A')
[2017-11-17 15:56:50,895] ## CB ## 6 (6, 'A')
[2017-11-17 15:56:50,896] ## AS ## (6, 'A')
[2017-11-17 15:56:53,831] Finished task_b_7
[2017-11-17 15:56:53,831] Started task_c_7
[2017-11-17 15:56:53,831] Started finalize-3256d0d8714e199e792484a0c0282daf
[2017-11-17 15:56:53,837] Finished task_b_5
[2017-11-17 15:56:53,837] Started task_c_5
[2017-11-17 15:56:53,838] Started finalize-62aef06ca2cbfcd1aabdeccc08c7675c
[2017-11-17 15:56:53,839] Finished task_c_4
[2017-11-17 15:56:53,839] Started task_d_4
[2017-11-17 15:56:53,840] Started finalize-a82334ec33a03f2a92d9f425738b4d99
[2017-11-17 15:56:53,841] Finished task_c_0
[2017-11-17 15:56:53,841] Started task_d_0
[2017-11-17 15:56:53,841] Started finalize-bb36957d5c82568435915d4e4e1f5a65
[2017-11-17 15:56:53,842] Finished finalize-d08efed1de50142f40fe9fea72076592
[2017-11-17 15:56:53,845] Finished task_c_8
[2017-11-17 15:56:53,845] Started task_d_8
[2017-11-17 15:56:53,845] Started finalize-194be04ecebe298cf700735e9895ea46
[2017-11-17 15:56:53,846] Finished finalize-51321a3e536dec5cdf3c65536251d7d0
[2017-11-17 15:56:53,847] Finished finalize-2e3d005089e6c6217a02872a432e889f
[2017-11-17 15:56:53,850] Finished task_c_3
[2017-11-17 15:56:53,850] Started task_d_3
[2017-11-17 15:56:53,851] Started finalize-51ba4a5f6149d00c030087128c278e07
[2017-11-17 15:56:53,851] Finished task_c_2
[2017-11-17 15:56:53,851] Started task_d_2
[2017-11-17 15:56:53,851] Started finalize-63f9f6575fb524243931cc5a5a4953bb
[2017-11-17 15:56:53,851] Finished finalize-3e143f8ddde23635b49f3c87f3f450db
[2017-11-17 15:56:53,854] Finished finalize-a1706f943ad889e73505df69b35777dd
[2017-11-17 15:56:53,855] ## AS ## (4, 'B')
[2017-11-17 15:56:53,857] ## CB ## 14 (4, 'B')
[2017-11-17 15:56:53,860] Finished task_c_6
[2017-11-17 15:56:53,860] Started task_d_6
[2017-11-17 15:56:53,860] Started finalize-aa69357827d0971009107f771763bd6a
[2017-11-17 15:56:53,862] Finished finalize-be02238df1a8cfed547aeecb9c523684
[2017-11-17 15:56:53,863] ## AS ## (8, 'B')
[2017-11-17 15:56:53,868] ## CB ## 18 (8, 'B')
[2017-11-17 15:56:53,869] ## AS ## (0, 'B')
[2017-11-17 15:56:53,873] ## CB ## 10 (0, 'B')
[2017-11-17 15:56:53,874] ## AS ## (2, 'B')
[2017-11-17 15:56:53,876] ## CB ## 12 (2, 'B')
[2017-11-17 15:56:53,880] ## AS ## (3, 'B')
[2017-11-17 15:56:53,882] ## CB ## 13 (3, 'B')
[2017-11-17 15:56:53,884] ## AS ## (6, 'B')
[2017-11-17 15:56:53,886] ## CB ## 16 (6, 'B')
[2017-11-17 15:56:56,835] Finished task_b_9
[2017-11-17 15:56:56,835] Started task_c_9
[2017-11-17 15:56:56,835] Started finalize-acc89c8750dacb11e390e32afe67a4cc
[2017-11-17 15:56:56,840] Finished task_b_1
[2017-11-17 15:56:56,841] Started task_c_1
[2017-11-17 15:56:56,841] Started finalize-b0be0c2430969ecdb2f3eff44070aeea
[2017-11-17 15:56:56,844] Finished task_d_4
[2017-11-17 15:56:56,844] Started finalize-d67a361d3fcdcd6f26e055ffae436f46
[2017-11-17 15:56:56,846] Finished task_d_0
[2017-11-17 15:56:56,846] Started finalize-c534e9cfd6c4e6e55beeade414b2f0c0
[2017-11-17 15:56:56,847] Finished finalize-a82334ec33a03f2a92d9f425738b4d99
[2017-11-17 15:56:56,847] Finished finalize-d67a361d3fcdcd6f26e055ffae436f46
[2017-11-17 15:56:56,849] Finished finalize-bb36957d5c82568435915d4e4e1f5a65
[2017-11-17 15:56:56,849] Finished finalize-c534e9cfd6c4e6e55beeade414b2f0c0
[2017-11-17 15:56:56,852] Finished task_d_8
[2017-11-17 15:56:56,852] Started finalize-5645944451dabe516462c7ea757083e6
[2017-11-17 15:56:56,854] ## AS ## (4, 'C')
[2017-11-17 15:56:56,856] ## CB ## 24 (4, 'C')
[2017-11-17 15:56:56,858] Finished finalize-194be04ecebe298cf700735e9895ea46
[2017-11-17 15:56:56,858] Finished finalize-5645944451dabe516462c7ea757083e6
[2017-11-17 15:56:56,860] Finished task_d_2
[2017-11-17 15:56:56,860] Started finalize-81d0c04f2cfea7bc2086a2390fb21692
[2017-11-17 15:56:56,860] Finished finalize-63f9f6575fb524243931cc5a5a4953bb
[2017-11-17 15:56:56,860] Finished task_d_3
[2017-11-17 15:56:56,861] Started finalize-7512865ecc042bdd84ec94849adcd181
[2017-11-17 15:56:56,861] Finished finalize-51ba4a5f6149d00c030087128c278e07
[2017-11-17 15:56:56,864] ## AS ## (4, 'D')
[2017-11-17 15:56:56,865] ## CB ## 34 (4, 'D')
[2017-11-17 15:56:56,865] Finished finalize-7512865ecc042bdd84ec94849adcd181
[2017-11-17 15:56:56,866] Finished finalize-81d0c04f2cfea7bc2086a2390fb21692
[2017-11-17 15:56:56,867] Finished task_d_6
[2017-11-17 15:56:56,867] Started finalize-74303381298d6007b1cb49d79cb49b10
[2017-11-17 15:56:56,868] Finished finalize-aa69357827d0971009107f771763bd6a
[2017-11-17 15:56:56,871] Finished finalize-74303381298d6007b1cb49d79cb49b10
[2017-11-17 15:56:56,873] ## AS ## (0, 'C')
[2017-11-17 15:56:56,875] ## CB ## 20 (0, 'C')
[2017-11-17 15:56:56,877] ## AS ## (0, 'D')
[2017-11-17 15:56:56,881] ## AS ## (8, 'C')
[2017-11-17 15:56:56,882] ## CB ## 30 (0, 'D')
[2017-11-17 15:56:56,886] ## AS ## (8, 'D')
[2017-11-17 15:56:56,888] ## CB ## 28 (8, 'C')
[2017-11-17 15:56:56,891] ## AS ## (2, 'C')
[2017-11-17 15:56:56,893] ## CB ## 38 (8, 'D')
[2017-11-17 15:56:56,895] ## AS ## (3, 'C')
[2017-11-17 15:56:56,898] ## CB ## 22 (2, 'C')
[2017-11-17 15:56:56,900] ## AS ## (3, 'D')
[2017-11-17 15:56:56,902] ## CB ## 23 (3, 'C')
[2017-11-17 15:56:56,906] ## AS ## (2, 'D')
[2017-11-17 15:56:56,909] ## CB ## 33 (3, 'D')
[2017-11-17 15:56:56,911] ## AS ## (6, 'C')
[2017-11-17 15:56:56,917] ## AS ## (6, 'D')
[2017-11-17 15:56:56,918] Finished finalize-dbdba2740eacf15e342f8f1772876e3e
[2017-11-17 15:56:56,921] Finished finalize-bbe837a7bbf44618969603228d694f74
[2017-11-17 15:56:56,922] ## CB ## 32 (2, 'D')
[2017-11-17 15:56:56,924] Finished finalize-db5bb4ece0ef14efc0d45702d77866dc
[2017-11-17 15:56:56,924] Finished finalize-cd366bd4ef7ae54971e1239001beb7d7
[2017-11-17 15:56:56,928] ## AS ## (7, 'A')
[2017-11-17 15:56:56,929] ## CB ## 26 (6, 'C')
[2017-11-17 15:56:56,933] ## AS ## (1, 'A')
[2017-11-17 15:56:56,934] ## CB ## 36 (6, 'D')
[2017-11-17 15:56:56,937] ## AS ## (9, 'A')
[2017-11-17 15:56:56,940] ## CB ## 7 (7, 'A')
[2017-11-17 15:56:56,942] ## AS ## (5, 'A')
[2017-11-17 15:56:56,944] ## CB ## 1 (1, 'A')
[2017-11-17 15:56:56,947] ## CB ## 9 (9, 'A')
[2017-11-17 15:56:56,950] ## CB ## 5 (5, 'A')
[2017-11-17 15:56:59,839] Finished task_c_7
[2017-11-17 15:56:59,839] Started task_d_7
[2017-11-17 15:56:59,839] Started finalize-933ff2776346fa534a4f2344847679e0
[2017-11-17 15:56:59,841] Finished finalize-acc89c8750dacb11e390e32afe67a4cc
[2017-11-17 15:56:59,841] Finished finalize-3256d0d8714e199e792484a0c0282daf
[2017-11-17 15:56:59,846] Finished task_c_5
[2017-11-17 15:56:59,846] Started task_d_5
[2017-11-17 15:56:59,846] Started finalize-1d9dd5f251005e492f4afea073f15e9f
[2017-11-17 15:56:59,851] ## CB ## 19 (9, 'B')
[2017-11-17 15:56:59,852] ## AS ## (9, 'B')
[2017-11-17 15:56:59,855] ## CB ## 17 (7, 'B')
[2017-11-17 15:56:59,856] ## AS ## (7, 'B')
[2017-11-17 15:56:59,920] Finished task_c_9
[2017-11-17 15:56:59,920] Started task_d_9
[2017-11-17 15:56:59,921] Started finalize-232232748ca6a607fa794f93e0e2d972
[2017-11-17 15:57:02,844] Finished task_d_7
[2017-11-17 15:57:02,844] Started finalize-4904651a3e035aa6b786784718b597e2
[2017-11-17 15:57:02,846] Finished finalize-933ff2776346fa534a4f2344847679e0
[2017-11-17 15:57:02,846] Finished finalize-4904651a3e035aa6b786784718b597e2
[2017-11-17 15:57:02,846] Finished task_c_1
[2017-11-17 15:57:02,847] Started task_d_1
[2017-11-17 15:57:02,847] Started finalize-9809461588b05210232840181bee4d15
[2017-11-17 15:57:02,851] ## AS ## (7, 'C')
[2017-11-17 15:57:02,854] ## CB ## 27 (7, 'C')
[2017-11-17 15:57:02,859] ## CB ## 37 (7, 'D')
[2017-11-17 15:57:02,859] ## AS ## (7, 'D')
[2017-11-17 15:57:02,926] Finished task_d_9
[2017-11-17 15:57:02,926] Started finalize-efcc04b422578fbf149c8fc103b0c9f3
[2017-11-17 15:57:02,928] Finished finalize-232232748ca6a607fa794f93e0e2d972
[2017-11-17 15:57:02,928] Finished finalize-efcc04b422578fbf149c8fc103b0c9f3
[2017-11-17 15:57:02,934] ## CB ## 29 (9, 'C')
[2017-11-17 15:57:02,937] ## AS ## (9, 'C')
[2017-11-17 15:57:02,940] ## CB ## 39 (9, 'D')
[2017-11-17 15:57:02,942] ## AS ## (9, 'D')
[2017-11-17 15:57:05,850] Finished task_d_5
[2017-11-17 15:57:05,850] Started finalize-30fab73cf4334eb2325154ee93422f05
[2017-11-17 15:57:05,852] Finished finalize-9809461588b05210232840181bee4d15
[2017-11-17 15:57:05,852] Finished finalize-1d9dd5f251005e492f4afea073f15e9f
[2017-11-17 15:57:05,852] Finished finalize-b0be0c2430969ecdb2f3eff44070aeea
[2017-11-17 15:57:05,856] Finished finalize-30fab73cf4334eb2325154ee93422f05
[2017-11-17 15:57:05,857] Finished finalize-62aef06ca2cbfcd1aabdeccc08c7675c
[2017-11-17 15:57:05,860] ## CB ## 21 (1, 'C')
[2017-11-17 15:57:05,862] ## AS ## (1, 'C')
[2017-11-17 15:57:05,865] ## CB ## 25 (5, 'C')
[2017-11-17 15:57:05,869] ## AS ## (5, 'C')
[2017-11-17 15:57:05,871] ## CB ## 11 (1, 'B')
[2017-11-17 15:57:05,876] ## AS ## (1, 'B')
[2017-11-17 15:57:05,877] ## CB ## 35 (5, 'D')
[2017-11-17 15:57:05,882] ## CB ## 15 (5, 'B')
[2017-11-17 15:57:05,882] ## AS ## (5, 'D')
[2017-11-17 15:57:05,884] ## AS ## (5, 'B')
[2017-11-17 15:57:05,912] Finished task_d_1
[2017-11-17 15:57:05,912] Started finalize-dee0218310beb3fafee2a14eb6fe5240
[2017-11-17 15:57:05,913] Finished finalize-dee0218310beb3fafee2a14eb6fe5240
[2017-11-17 15:57:05,919] ## AS ## (1, 'D')
[2017-11-17 15:57:05,927] ## CB ## 31 (1, 'D')
```
</p>
</details>

The issue is that futures are marked as ready much later than when the corresponding task moves to the `memory` state when the result should already be available.

Did I miss something? Or is there a genuine defect?
Is there any way to get intermediate results right when they become ready?


Discussion:
For a slight improvement you might consider using the `with_results=` keyword to `as_completed`.  See http://dask.pydata.org/en/latest/futures.html#waiting-on-futures

But generally no, currently there are a few network hops between a task being marked as finished on the scheduler and the client getting the results.  Today the process looks a bit like the following:

1.  Worker informs scheduler that task has finished
2.  Scheduler informs client that task has finished
3.  Then one of the following:
    1.  Client requests result from scheduler
    2.  Scheduler requests result from worker
    3.  Worker sends result to scheduler
    4.  Scheduler sends result to client
4.  Or...
    1.  Client requests addresses of workers who have data from scheduler
    2.  Scheduler sends addresses to client
    3.  Client requests data from worker directly
    4.  Worker sends data directly

All of this typically happens in around 5-10ms (depending on your network).  For some applications this is fine, for others, it's a very long time.  I can imagine this improving in the future.  Historically people haven't cared much.  I'd be curious to know more about applications that do care about millisecond response latency.

Thanks for the hint on `with_results` and for the workflow explanation.
However, you describe a delay of 5-10ms but in my case, it's proportional to `dt`, which can be seconds. Of course, in the real code, I don't have any `dt` - it's just the computations that can take a significant amount of time to complete.
I assume that, given the described workflow, such delay is caused by the workers not being able to respond with the result because they're already busy with a computation.

Do you have any ideas on how to improve the situation? Off the top of my head, I think it should be possible to either (1) optionally reserve a thread on each worker specifically to fetch results or (2) let workers know what results will be needed, so the workers can send the results to the scheduler right away, without waiting for a request. What do you think?
Tasks do run in a separate thread pool.  There is a separate thread for administration and communication. 

When you call compute on a delayed object (or any dask colleciton) it also generates a finalize task which, in the case of delayed objects is really just a no-op.  My guess is that priorities work out such that A runs, then B runs, then C runs, then D runs, finally A's finalize gets to run, B's finalize gets to run, etc..  If so, then this is a fail case of task scheduling.  We might consider ways to remove the finalize task (which, as I mentioned, is useless in the case of dask delayed) or we might consider prioritizing outputs of the graph more highly than intermediate nodes.

The above explanation is just a guess though.  I may have some time to look at this more deeply tomorrow.
@jcrist, if my assessment is correct then do you have any thoughts about a convention around `__dask_postcompute__` that would be sensible for collections that may not require a finalize call like delayed?  Things are a little bit tricky here because we *do* need to unpack the singleton key.
As a side note - `finalize` tasks hide names of the underlying tasks, which makes it impossible to use `Future.add_done_callback` in the cases where I need to know the corresponding task.

Resolving patch:
diff --git a/distributed/client.py b/distributed/client.py
--- a/distributed/client.py
+++ b/distributed/client.py
@@ -29,7 +29,14 @@
 from dask.core import flatten, get_dependencies
 from dask.compatibility import apply, unicode
 from dask.context import _globals
-from toolz import first, groupby, merge, valmap, keymap
+try:
+    from cytoolz import first, groupby, merge, valmap, keymap
+except ImportError:
+    from toolz import first, groupby, merge, valmap, keymap
+try:
+    from dask.delayed import single_key
+except ImportError:
+    single_key = first
 from tornado import gen
 from tornado.gen import TimeoutError
 from tornado.locks import Event, Condition
@@ -2113,9 +2120,13 @@ def compute(self, collections, sync=False, optimize_graph=True,
         dsk = self.collections_to_dsk(variables, optimize_graph, **kwargs)
         names = ['finalize-%s' % tokenize(v) for v in variables]
         dsk2 = {}
-        for name, v in zip(names, variables):
+        for i, (name, v) in enumerate(zip(names, variables)):
             func, extra_args = v.__dask_postcompute__()
-            dsk2[name] = (func, v.__dask_keys__()) + extra_args
+            keys = v.__dask_keys__()
+            if func is single_key and len(keys) == 1 and not extra_args:
+                names[i] = keys[0]
+            else:
+                dsk2[name] = (func, keys) + extra_args
 
         restrictions, loose_restrictions = self.get_restrictions(collections,
                                                                  workers, allow_other_workers)
@@ -2813,7 +2824,8 @@ def get_versions(self, check=False):
 
         if check:
             # we care about the required & optional packages matching
-            def to_packages(d): return merge(d['packages'].values())
+            def to_packages(d):
+                return dict(sum(d['packages'].values(), []))
             client_versions = to_packages(result['client'])
             versions = [('scheduler', to_packages(result['scheduler']))]
             versions.extend((w, to_packages(d))


Test patch:
diff --git a/distributed/diagnostics/tests/test_progress_stream.py b/distributed/diagnostics/tests/test_progress_stream.py
--- a/distributed/diagnostics/tests/test_progress_stream.py
+++ b/distributed/diagnostics/tests/test_progress_stream.py
@@ -63,10 +63,10 @@ def test_progress_stream(c, s, a, b):
     comm = yield progress_stream(s.address, interval=0.010)
     msg = yield comm.read()
     nbytes = msg.pop('nbytes')
-    assert msg == {'all': {'div': 10, 'inc': 5, 'finalize': 1},
+    assert msg == {'all': {'div': 10, 'inc': 5},
                    'erred': {'div': 1},
-                   'memory': {'div': 9, 'finalize': 1},
-                   'released': {'inc': 5}}
+                   'memory': {'div': 9, 'inc': 1},
+                   'released': {'inc': 4}}
     assert set(nbytes) == set(msg['all'])
     assert all(v > 0 for v in nbytes.values())
 
diff --git a/distributed/tests/test_client.py b/distributed/tests/test_client.py
--- a/distributed/tests/test_client.py
+++ b/distributed/tests/test_client.py
@@ -5039,5 +5039,14 @@ def test_logs(c, s, a, b):
             assert 'distributed.worker' in msg
 
 
+@gen_cluster(client=True)
+def test_avoid_delayed_finalize(c, s, a, b):
+    x = delayed(inc)(1)
+    future = c.compute(x)
+    result = yield future
+    assert result == 2
+    assert list(s.tasks) == [future.key] == [x.key]
+
+
 if sys.version_info >= (3, 5):
     from distributed.tests.py3_test_client import *  # flake8: noqa