| 
					
				 | 
			
			
				@@ -12,10 +12,13 @@ logger = create_logger() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from .helpers import Error, get_cache_dir, decode_dict, int_to_bigint, \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     bigint_to_int, format_file_size, yes 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from .locking import UpgradableLock 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-from .hashindex import ChunkIndex 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from .hashindex import ChunkIndex, ChunkIndexEntry 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import msgpack 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ChunkListEntry = namedtuple('ChunkListEntry', 'id size csize') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+FileCacheEntry = namedtuple('FileCacheEntry', 'age inode size mtime chunk_ids') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 class Cache: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     """Client Side cache 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -183,9 +186,9 @@ Chunk index:    {0.total_unique_chunks:20d} {0.total_chunks:20d}""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 u.feed(data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 for path_hash, item in u: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    item[0] += 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    entry = FileCacheEntry(*item) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     # in the end, this takes about 240 Bytes per file 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    self.files[path_hash] = msgpack.packb(item) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    self.files[path_hash] = msgpack.packb(entry._replace(age=entry.age + 1)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def begin_txn(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         # Initialize transaction snapshot 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -208,9 +211,9 @@ Chunk index:    {0.total_unique_chunks:20d} {0.total_chunks:20d}""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 for path_hash, item in self.files.items(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     # Discard cached files with the newest mtime to avoid 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     # issues with filesystem snapshots and mtime precision 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    item = msgpack.unpackb(item) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if item[0] < 10 and bigint_to_int(item[3]) < self._newest_mtime: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        msgpack.pack((path_hash, item), fd) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    entry = FileCacheEntry(*msgpack.unpackb(item)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if entry.age < 10 and bigint_to_int(entry.mtime) < self._newest_mtime: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        msgpack.pack((path_hash, entry), fd) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self.config.set('cache', 'manifest', hexlify(self.manifest.id).decode('ascii')) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self.config.set('cache', 'timestamp', self.manifest.timestamp) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self.config.set('cache', 'key_type', str(self.key.TYPE)) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -375,12 +378,12 @@ Chunk index:    {0.total_unique_chunks:20d} {0.total_chunks:20d}""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         data = self.key.encrypt(data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         csize = len(data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self.repository.put(id, data, wait=False) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.chunks[id] = (refcount + 1, size, csize) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.chunks[id] = ChunkIndexEntry(refcount + 1, size, csize) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         stats.update(size, csize, True) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return id, size, csize 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return ChunkListEntry(id, size, csize) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def seen_chunk(self, id, size=None): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        refcount, stored_size, _ = self.chunks.get(id, (0, None, None)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        refcount, stored_size, _ = self.chunks.get(id, ChunkIndexEntry(0, None, None)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if size is not None and stored_size is not None and size != stored_size: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             # we already have a chunk with that id, but different size. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             # this is either a hash collision (unlikely) or corruption or a bug. 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -393,7 +396,7 @@ Chunk index:    {0.total_unique_chunks:20d} {0.total_chunks:20d}""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             self.begin_txn() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         count, size, csize = self.chunks.incref(id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         stats.update(size, csize, False) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return id, size, csize 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return ChunkListEntry(id, size, csize) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def chunk_decref(self, id, stats): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if not self.txn_active: 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -414,20 +417,17 @@ Chunk index:    {0.total_unique_chunks:20d} {0.total_chunks:20d}""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         entry = self.files.get(path_hash) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if not entry: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        entry = msgpack.unpackb(entry) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if (entry[2] == st.st_size and bigint_to_int(entry[3]) == st.st_mtime_ns and 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                (ignore_inode or entry[1] == st.st_ino)): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            # reset entry age 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            entry[0] = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            self.files[path_hash] = msgpack.packb(entry) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return entry[4] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        entry = FileCacheEntry(*msgpack.unpackb(entry)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (entry.size == st.st_size and bigint_to_int(entry.mtime) == st.st_mtime_ns and 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                (ignore_inode or entry.inode == st.st_ino)): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self.files[path_hash] = msgpack.packb(entry._replace(age=0)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return entry.chunk_ids 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def memorize_file(self, path_hash, st, ids): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if not (self.do_files and stat.S_ISREG(st.st_mode)): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        # Entry: Age, inode, size, mtime, chunk ids 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        mtime_ns = st.st_mtime_ns 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self.files[path_hash] = msgpack.packb((0, st.st_ino, st.st_size, int_to_bigint(mtime_ns), ids)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        self._newest_mtime = max(self._newest_mtime, mtime_ns) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        entry = FileCacheEntry(age=0, inode=st.st_ino, size=st.st_size, mtime=int_to_bigint(st.st_mtime_ns), chunk_ids=ids) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.files[path_hash] = msgpack.packb(entry) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._newest_mtime = max(self._newest_mtime, st.st_mtime_ns) 
			 |