Repository: dask/distributed

Issue:
distributed.scheduler - WARNING - Bad message: op=queue_release
Run following code in Jupyter notebook:

```
from dask.distributed import Client, Queue
client = Client()

def test_queue(q):
    import time
    for i in range(10):
        q.put(i)
        time.sleep(0.2)

def test():
    import time
    q = Queue()
    f = client.submit(test_queue, q)
    while True:
        if f.done():
            print(q.get(batch=True))
            break
        print(q.get(batch=True))
        time.sleep(0.1)

test()
```
It will output two Bad message warning:

```
[]
[0]
[]
[1]
[]
[2]
[]
[3]
[4]
[]
[5]
[]
[6]
[]
[7]
[]
[8]
[]
[9]
[]
[]

distributed.scheduler - WARNING - Bad message: op=queue_release, {'name': 'queue-9ce26f341ebc416ebc0f46e26a25f6fd'}
NoneType: None
distributed.scheduler - WARNING - Bad message: op=queue_release, {'name': 'queue-9ce26f341ebc416ebc0f46e26a25f6fd'}
NoneType: None
```


Discussion:

Resolving patch:
diff --git a/distributed/queues.py b/distributed/queues.py
--- a/distributed/queues.py
+++ b/distributed/queues.py
@@ -32,13 +32,17 @@ def __init__(self, scheduler):
         self.client_refcount = dict()
         self.future_refcount = defaultdict(lambda: 0)
 
-        self.scheduler.handlers.update({'queue_create': self.create,
-                                        'queue_release': self.release,
-                                        'queue_put': self.put,
-                                        'queue_get': self.get,
-                                        'queue_qsize': self.qsize})
-
-        self.scheduler.client_handlers['queue-future-release'] = self.future_release
+        self.scheduler.handlers.update({
+            'queue_create': self.create,
+            'queue_put': self.put,
+            'queue_get': self.get,
+            'queue_qsize': self.qsize}
+        )
+
+        self.scheduler.client_handlers.update({
+            'queue-future-release': self.future_release,
+            'queue_release': self.release,
+        })
 
         self.scheduler.extensions['queues'] = self
 
@@ -50,13 +54,18 @@ def create(self, stream=None, name=None, client=None, maxsize=0):
             self.client_refcount[name] += 1
 
     def release(self, stream=None, name=None, client=None):
+        if name not in self.queues:
+            return
+
         self.client_refcount[name] -= 1
         if self.client_refcount[name] == 0:
             del self.client_refcount[name]
-            futures = self.queues[name].queue
+            futures = self.queues[name]._queue
             del self.queues[name]
-            self.scheduler.client_releases_keys(keys=[f.key for f in futures],
-                                                client='queue-%s' % name)
+            self.scheduler.client_releases_keys(
+                    keys=[d['value'] for d in futures if d['type'] == 'Future'],
+                    client='queue-%s' % name
+            )
 
     @gen.coroutine
     def put(self, stream=None, name=None, key=None, data=None, client=None, timeout=None):
@@ -232,14 +241,11 @@ def _qsize(self):
         result = yield self.client.scheduler.queue_qsize(name=self.name)
         raise gen.Return(result)
 
-    def _release(self):
+    def close(self):
         if self.client.status == 'running':  # TODO: can leave zombie futures
             self.client._send_to_scheduler({'op': 'queue_release',
                                             'name': self.name})
 
-    def __del__(self):
-        self._release()
-
     def __getstate__(self):
         return (self.name, self.client.scheduler.address)
 


Test patch:
diff --git a/distributed/tests/test_queues.py b/distributed/tests/test_queues.py
--- a/distributed/tests/test_queues.py
+++ b/distributed/tests/test_queues.py
@@ -240,3 +240,17 @@ def test_erred_future(c, s, a, b):
 
     exc = yield future2.exception()
     assert isinstance(exc, ZeroDivisionError)
+
+
+@gen_cluster(client=True)
+def test_close(c, s, a, b):
+    q = Queue()
+
+    while q.name not in s.extensions['queues'].queues:
+        yield gen.sleep(0.01)
+
+    q.close()
+    q.close()
+
+    while q.name in s.extensions['queues'].queues:
+        yield gen.sleep(0.01)