codecov-io: # [Codecov](https://codecov.io/gh/seung-lab/cloud-volume/pull/343?src=pr&el=h1) Report
> Merging [#343](https://codecov.io/gh/seung-lab/cloud-volume/pull/343?src=pr&el=desc) into [master](https://codecov.io/gh/seung-lab/cloud-volume/commit/c3556ac9116f474c76d0046263e4b57ec0af3388&el=desc) will **increase** coverage by `0.02%`.
> The diff coverage is `79.16%`.

[![Impacted file tree graph](https://codecov.io/gh/seung-lab/cloud-volume/pull/343/graphs/tree.svg?width=650&height=150&src=pr&token=7WwqdI3qkW)](https://codecov.io/gh/seung-lab/cloud-volume/pull/343?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #343      +/-   ##
==========================================
+ Coverage   79.14%   79.16%   +0.02%     
==========================================
  Files          75       75              
  Lines        9081     9121      +40     
==========================================
+ Hits         7187     7221      +34     
- Misses       1894     1900       +6     
```


| [Impacted Files](https://codecov.io/gh/seung-lab/cloud-volume/pull/343?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [cloudvolume/lru.py](https://codecov.io/gh/seung-lab/cloud-volume/pull/343/diff?src=pr&el=tree#diff-Y2xvdWR2b2x1bWUvbHJ1LnB5) | `69.31% <69.69%> (+1.01%)` | :arrow_up: |
| [test/test\_lru.py](https://codecov.io/gh/seung-lab/cloud-volume/pull/343/diff?src=pr&el=tree#diff-dGVzdC90ZXN0X2xydS5weQ==) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/seung-lab/cloud-volume/pull/343?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/seung-lab/cloud-volume/pull/343?src=pr&el=footer). Last update [c3556ac...ae01e39](https://codecov.io/gh/seung-lab/cloud-volume/pull/343?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

diff --git a/cloudvolume/lru.py b/cloudvolume/lru.py
index 161ccfb..df80ede 100644
--- a/cloudvolume/lru.py
+++ b/cloudvolume/lru.py
@@ -100,6 +100,11 @@ class DoublyLinkedList(object):
   def delete(self, node):
     nxt, prev = node.next, node.prev
 
+    if self.head == node:
+      self.head = nxt
+    if self.tail == node:
+      self.tail = prev
+
     if prev is not None:
       prev.next = nxt
 
@@ -133,11 +138,12 @@ class DoublyLinkedList(object):
       self.head = ListNode(val, None, None)
       self.tail = self.head
     elif self.head is None:
-      self.head = ListNode(val, self.tail, None)
+      self.head = ListNode(val, next=self.tail, prev=None)
       self.tail.prev = self.head
     else:
-      self.head = ListNode(val, self.head, None)
-      self.head.next.prev = self.head
+      prev_head = self.head
+      self.head = ListNode(val, next=prev_head, prev=None)
+      prev_head.prev = self.head
 
     self.size += 1
 
@@ -170,6 +176,15 @@ class LRU(object):
   def __len__(self):
     return self.queue.size
 
+  def keys(self):
+    return self.hash.keys()
+
+  def values(self):
+    return ( node.val for val in self.queue )
+
+  def items(self):
+    return ( (key, node.val) for key, node in self.hash.items() )
+
   def clear(self):
     self.queue = DoublyLinkedList()
     self.hash = {}
@@ -190,19 +205,26 @@ class LRU(object):
       (key,val) = self.queue.delete_tail()
       del self.hash[key]
 
-  def __contains__(self, key):
-    return key in self.hash
-
-  def __getitem__(self, key):
+  def delete(self, key):
     if key not in self.hash:
       raise KeyError("{} not in cache.".format(key))
 
+    node = self.hash[key]
+    self.queue.delete(node)
+    del self.hash[key]
+
+  def get(self, key, default=None):
+    if key not in self.hash:
+      if default is None:
+        raise KeyError("{} not in cache.".format(key))
+      return default
+
     node = self.hash[key]
     self.queue.promote_to_head(node)
 
     return node.val[1]
 
-  def __setitem__(self, key, val):
+  def set(self, key, val):
     if self.size == 0:
       return
 
@@ -218,7 +240,19 @@ class LRU(object):
 
     while len(self.queue) > self.size:
       (tkey,tval) = self.queue.delete_tail()
-      del self.hash[tkey]      
+      del self.hash[tkey]     
+
+  def __contains__(self, key):
+    return key in self.hash
+
+  def __getitem__(self, key):
+    return self.get(key)
+
+  def __setitem__(self, key, val):
+    return self.set(key, val)
+
+  def __delitem__(self, key):
+    return self.delete(key)
 
   def __str__(self):
     return str(self.queue)

LRU Crash
The new LRU code is crashing on basil:

cv = CloudVolume('...')

img = cv.download_point((37902, 50771, 610), size=(256,256,256))
labels = fastremap.unique(img)
print(len(labels))

s = time.time()
x = cv.skeleton.reader.exists(labels, cv.skeleton.meta.skeleton_path)
print(time.time() - s)

```
Traceback (most recent call last):
  File "test2.py", line 17, in <module>
    x = cv.skeleton.reader.exists(labels, cv.skeleton.meta.skeleton_path)
  File "/Users/wms/cloud-volume/cloudvolume/datasource/precomputed/sharding.py", line 407, in exists
    minishard_number, path
  File "/Users/wms/cloud-volume/cloudvolume/datasource/precomputed/sharding.py", line 309, in get_minishard_index
    res = self.get_minishard_indices(filename, index, minishard_no, path)
  File "/Users/wms/cloud-volume/cloudvolume/datasource/precomputed/sharding.py", line 360, in get_minishard_indices
    self.minishard_index_cache[cache_key] = minishard_index
  File "/Users/wms/cloud-volume/cloudvolume/lru.py", line 221, in __setitem__
    (tkey, tval) = self.queue.delete_tail()
TypeError: cannot unpack non-iterable NoneType object
 92%|█████████████████████████████████████▌   | 457/499 [02:02<00:11,  3.73it/s]```
diff --git a/test/test_lru.py b/test/test_lru.py
index b5601f8..7031452 100644
--- a/test/test_lru.py
+++ b/test/test_lru.py
@@ -1,5 +1,10 @@
+import pytest
+
 from cloudvolume.lru import LRU
 
+import time
+import random
+
 def test_lru():
   lru = LRU(5)
 
@@ -34,3 +39,25 @@ def test_lru():
   assert lru.queue.tolist() == [ 
     (0,0), (4,4), (3,3), (2,2), (1,1)
   ]
+
+def test_lru_chaos():
+  lru = LRU(10)
+
+  seed = time.time()
+  print("seed", seed)
+
+  random.seed(seed)
+  for i in range(150):
+    rand = random.randint(-5000, 5000)
+    lru[rand] = random.randint(-5000, 5000)
+
+    if rand % 17 == 0:
+      keys = lru.keys()
+      key = random.choice(list(keys))
+      lru[key]
+
+
+
+
+
+

pip install -e .[all_viewers]
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-xdist pytest-mock pytest-asyncio
apt-get update apt-get install -y gcc g++ make pkg-config
3.9
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==25.3.0
boto3==1.37.23
botocore==1.37.23
Brotli==1.1.0
cachetools==5.5.2
certifi==2025.1.31
cffi==1.17.1
chardet==5.2.0
charset-normalizer==3.4.1
click==8.1.8
-e git+https://github.com/seung-lab/cloud-volume.git@c3556ac9116f474c76d0046263e4b57ec0af3388#egg=cloud_volume
compressed_segmentation==2.3.2
contourpy==1.3.0
coverage==7.8.0
cryptography==44.0.2
cycler==0.12.1
DracoPy==1.5.0
exceptiongroup==1.2.2
execnet==2.1.1
fastremap==1.15.1
fonttools==4.56.0
fpzip==1.2.5
gevent==24.11.1
google-api-core==2.24.2
google-auth==2.38.0
google-cloud-core==2.4.3
google-cloud-storage==3.1.0
google-crc32c==1.7.1
google-resumable-media==2.7.2
googleapis-common-protos==1.69.2
greenlet==3.1.1
idna==3.10
importlib_metadata==8.6.1
importlib_resources==6.5.2
inflection==0.5.1
iniconfig==2.1.0
jmespath==1.0.1
json5==0.10.0
jsonschema==4.23.0
jsonschema-specifications==2024.10.1
kiwisolver==1.4.7
Markdown==3.7
matplotlib==3.9.4
numpy==2.0.2
packaging==24.2
pillow==11.1.0
pluggy==1.5.0
posix_ipc==1.0.4
proto-plus==1.26.1
protobuf==6.30.2
psutil==5.4.3
pyasn1==0.6.1
pyasn1_modules==0.4.2
pycparser==2.22
pyOpenSSL==25.0.0
pyparsing==3.2.3
pytest==8.3.5
pytest-asyncio==0.26.0
pytest-cov==6.0.0
pytest-mock==3.14.0
pytest-xdist==3.6.1
python-dateutil==2.9.0.post0
python-jsonschema-objects==0.5.7
referencing==0.36.2
requests==2.32.3
rpds-py==0.24.0
rsa==4.9
s3transfer==0.11.4
six==1.17.0
tenacity==9.0.0
tomli==2.2.1
tqdm==4.67.1
typing_extensions==4.13.0
urllib3==1.26.20
urllib3-secure-extra==0.1.0
vtk==9.4.2
zipp==3.21.0
zope.event==5.0
zope.interface==7.2

name: cloud-volume
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==25.3.0
      - boto3==1.37.23
      - botocore==1.37.23
      - brotli==1.1.0
      - cachetools==5.5.2
      - certifi==2025.1.31
      - cffi==1.17.1
      - chardet==5.2.0
      - charset-normalizer==3.4.1
      - click==8.1.8
      - compressed-segmentation==2.3.2
      - contourpy==1.3.0
      - coverage==7.8.0
      - cryptography==44.0.2
      - cycler==0.12.1
      - dracopy==1.5.0
      - exceptiongroup==1.2.2
      - execnet==2.1.1
      - fastremap==1.15.1
      - fonttools==4.56.0
      - fpzip==1.2.5
      - gevent==24.11.1
      - google-api-core==2.24.2
      - google-auth==2.38.0
      - google-cloud-core==2.4.3
      - google-cloud-storage==3.1.0
      - google-crc32c==1.7.1
      - google-resumable-media==2.7.2
      - googleapis-common-protos==1.69.2
      - greenlet==3.1.1
      - idna==3.10
      - importlib-metadata==8.6.1
      - importlib-resources==6.5.2
      - inflection==0.5.1
      - iniconfig==2.1.0
      - jmespath==1.0.1
      - json5==0.10.0
      - jsonschema==4.23.0
      - jsonschema-specifications==2024.10.1
      - kiwisolver==1.4.7
      - markdown==3.7
      - matplotlib==3.9.4
      - numpy==2.0.2
      - packaging==24.2
      - pillow==11.1.0
      - pluggy==1.5.0
      - posix-ipc==1.0.4
      - proto-plus==1.26.1
      - protobuf==6.30.2
      - psutil==5.4.3
      - pyasn1==0.6.1
      - pyasn1-modules==0.4.2
      - pycparser==2.22
      - pyopenssl==25.0.0
      - pyparsing==3.2.3
      - pytest==8.3.5
      - pytest-asyncio==0.26.0
      - pytest-cov==6.0.0
      - pytest-mock==3.14.0
      - pytest-xdist==3.6.1
      - python-dateutil==2.9.0.post0
      - python-jsonschema-objects==0.5.7
      - referencing==0.36.2
      - requests==2.32.3
      - rpds-py==0.24.0
      - rsa==4.9
      - s3transfer==0.11.4
      - six==1.17.0
      - tenacity==9.0.0
      - tomli==2.2.1
      - tqdm==4.67.1
      - typing-extensions==4.13.0
      - urllib3==1.26.20
      - urllib3-secure-extra==0.1.0
      - vtk==9.4.2
      - zipp==3.21.0
      - zope-event==5.0
      - zope-interface==7.2
prefix: /opt/conda/envs/cloud-volume

test/test_lru.py::test_lru_chaos
test/test_lru.py::test_lru
cloudvolume/lru.py
cloudvolume/lru.py