From 2a05aa2d9142ceea8a78c4f12b1be29794115c27 Mon Sep 17 00:00:00 2001
From: Brian Warner <warner@lothar.com>
Date: Wed, 4 Aug 2010 00:28:08 -0700
Subject: [PATCH] lazily create DownloadNode upon first read()/get_segment()

---
 src/allmydata/immutable/filenode.py    | 16 ++++++++++++++--
 src/allmydata/test/test_download.py    |  7 +++++++
 src/allmydata/test/test_hung_server.py |  1 +
 3 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/src/allmydata/immutable/filenode.py b/src/allmydata/immutable/filenode.py
index 1d5be948..44c8e95e 100644
--- a/src/allmydata/immutable/filenode.py
+++ b/src/allmydata/immutable/filenode.py
@@ -31,14 +31,24 @@ class CiphertextFileNode:
             if history:
                 history.add_download(ds)
             download_status = ds
-        self._node = DownloadNode(verifycap, storage_broker, secret_holder,
-                                  terminator, history, download_status)
+        self._terminator = terminator
+        self._history = history
+        self._download_status = download_status
+        self._node = None # created lazily, on read()
+
+    def _maybe_create_download_node(self):
+        if self._node is None:
+            self._node = DownloadNode(self._verifycap, self._storage_broker,
+                                      self._secret_holder,
+                                      self._terminator,
+                                      self._history, self._download_status)
 
     def read(self, consumer, offset=0, size=None, read_ev=None):
         """I am the main entry point, from which FileNode.read() can get
         data. I feed the consumer with the desired range of ciphertext. I
         return a Deferred that fires (with the consumer) when the read is
         finished."""
+        self._maybe_create_download_node()
         return self._node.read(consumer, offset, size, read_ev)
 
     def get_segment(self, segnum):
@@ -56,10 +66,12 @@ class CiphertextFileNode:
         segment, so that you can call get_segment() before knowing the
         segment size, and still know which data you received.
         """
+        self._maybe_create_download_node()
         return self._node.get_segment(segnum)
 
     def get_segment_size(self):
         # return a Deferred that fires with the file's real segment size
+        self._maybe_create_download_node()
         return self._node.get_segsize()
 
     def get_storage_index(self):
diff --git a/src/allmydata/test/test_download.py b/src/allmydata/test/test_download.py
index 570a1df1..42d3c696 100644
--- a/src/allmydata/test/test_download.py
+++ b/src/allmydata/test/test_download.py
@@ -327,6 +327,7 @@ class DownloadTest(_Base, unittest.TestCase):
         self.c0 = self.g.clients[0]
         self.load_shares()
         n = self.c0.create_node_from_uri(immutable_uri)
+        n._cnode._maybe_create_download_node()
 
         # Cause the downloader to guess a segsize that's too low, so it will
         # ask for a segment number that's too high (beyond the end of the
@@ -385,6 +386,7 @@ class DownloadTest(_Base, unittest.TestCase):
         d = self.c0.upload(u)
         def _uploaded(ur):
             n = self.c0.create_node_from_uri(ur.uri)
+            n._cnode._maybe_create_download_node()
             n._cnode._node._build_guessed_tables(u.max_segment_size)
             d1 = n.read(con1, 70, 20)
             #d2 = n.read(con2, 140, 20) # XXX
@@ -413,6 +415,7 @@ class DownloadTest(_Base, unittest.TestCase):
         d = self.c0.upload(u)
         def _uploaded(ur):
             n = self.c0.create_node_from_uri(ur.uri)
+            n._cnode._maybe_create_download_node()
             n._cnode._node._build_guessed_tables(u.max_segment_size)
             d = n.read(con1, 12000, 20)
             def _read1(ign):
@@ -620,6 +623,7 @@ class DownloadTest(_Base, unittest.TestCase):
         d = self.c0.upload(u)
         def _uploaded(ur):
             n = self.c0.create_node_from_uri(ur.uri)
+            n._cnode._maybe_create_download_node()
             n._cnode._node._build_guessed_tables(u.max_segment_size)
 
             d = download_to_data(n)
@@ -658,6 +662,7 @@ class DownloadTest(_Base, unittest.TestCase):
         d = self.c0.upload(u)
         def _uploaded(ur):
             n = self.c0.create_node_from_uri(ur.uri)
+            n._cnode._maybe_create_download_node()
             n._cnode._node._build_guessed_tables(u.max_segment_size)
             d1 = n.read(con1, 70, 20)
             #d2 = n.read(con2, 140, 20)
@@ -805,6 +810,7 @@ class Corruption(_Base, unittest.TestCase):
 
         def _download(ign, imm_uri, which, expected):
             n = self.c0.create_node_from_uri(imm_uri)
+            n._cnode._maybe_create_download_node()
             # for this test to work, we need to have a new Node each time.
             # Make sure the NodeMaker's weakcache hasn't interfered.
             assert not n._cnode._node._shares
@@ -945,6 +951,7 @@ class Corruption(_Base, unittest.TestCase):
                           ]
             def _download(imm_uri):
                 n = self.c0.create_node_from_uri(imm_uri)
+                n._cnode._maybe_create_download_node()
                 # for this test to work, we need to have a new Node each time.
                 # Make sure the NodeMaker's weakcache hasn't interfered.
                 assert not n._cnode._node._shares
diff --git a/src/allmydata/test/test_hung_server.py b/src/allmydata/test/test_hung_server.py
index 24dc8467..bc331c21 100644
--- a/src/allmydata/test/test_hung_server.py
+++ b/src/allmydata/test/test_hung_server.py
@@ -231,6 +231,7 @@ class HungServerDownloadTest(GridTestMixin, ShouldFailMixin, PollMixin,
         def _reduce_max_outstanding_requests_and_download(ign):
             self._hang_shares(range(5))
             n = self.c0.create_node_from_uri(self.uri)
+            n._cnode._maybe_create_download_node()
             self._sf = n._cnode._node._sharefinder
             self._sf.max_outstanding_requests = 5
             self._sf.OVERDUE_TIMEOUT = 1000.0
-- 
2.45.2