flatbuffer_index.py (3937B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\imagecaching\client\repository\flatbuffer_index.py 2 import os 3 import blue 4 import flatbuffers 5 import uthread2 6 from locks import FifoLock, TempLock 7 from imagecaching.client.model.definitions.image.entry import ImageEntry 8 from imagecaching.client.model.definitions.image.identifier import ImageIdentifier 9 from imagecaching.client.model.flatbuffer.image_cache_generated import ImageList, ImageListStart, ImageListAddImages, ImageListStartImagesVector, ImageListEnd 10 from imagecaching.client.const import INDEX_FILENAME 11 if False: 12 from typing import Dict, Optional 13 14 def _resolve_path_for_writing(res_path): 15 return blue.paths.ResolvePathForWriting(res_path) 16 17 18 class FolderIndex(object): 19 RETRY_DELAY = 0.1 20 RETRY_ATTEMPTS = 5 21 22 def __init__(self, folder_res_path): 23 self._folder_res_path = folder_res_path 24 self._entries = {} 25 self._retry_attempts = FolderIndex.RETRY_ATTEMPTS 26 27 @property 28 def folder_res_path(self): 29 return self._folder_res_path 30 31 @property 32 def index_res_path(self): 33 return u'%s/%s' % (self._folder_res_path, INDEX_FILENAME) 34 35 def get(self, image_id): 36 return self._entries.get(image_id) 37 38 def identifiers(self): 39 return sorted(self._entries, key=lambda identifier: identifier.image_id) 40 41 def upsert(self, image_entry): 42 self._entries[image_entry.identifier] = image_entry 43 44 def remove(self, image_id): 45 return self._entries.pop(image_id, None) is not None 46 47 def from_bytes(self, buf): 48 self._entries = {} 49 if not buf: 50 return 51 root = ImageList.GetRootAs(buf, 0) 52 for i in range(root.ImagesLength()): 53 entry = ImageEntry.from_flatbuffer(root.Images(i)) 54 self._entries[entry.identifier] = entry 55 56 def to_bytes(self): 57 builder = flatbuffers.Builder(0) 58 image_offsets = [ entry.to_flatbuffer(builder) for entry in self._entries.values() ] 59 ImageListStartImagesVector(builder, len(image_offsets)) 60 for offset in reversed(image_offsets): 61 builder.PrependUOffsetTRelative(offset) 62 63 images_vector = builder.EndVector() 64 ImageListStart(builder) 65 ImageListAddImages(builder, images_vector) 66 root = ImageListEnd(builder) 67 builder.Finish(root) 68 return builder.Output() 69 70 def _access_lock(self): 71 return TempLock(self.index_res_path, FifoLock) 72 73 def _retry_wrapper(self, impl_method, *args, **kwargs): 74 with self._access_lock(): 75 for attempt in range(self._retry_attempts): 76 try: 77 return impl_method(*args, **kwargs) 78 except (OSError, IOError): 79 if attempt < self._retry_attempts - 1: 80 uthread2.Sleep(FolderIndex.RETRY_DELAY) 81 else: 82 raise 83 84 def load(self): 85 self._retry_wrapper(self._load_impl) 86 87 def _load_impl(self): 88 path = _resolve_path_for_writing(self.index_res_path) 89 if not os.path.exists(path): 90 self._entries = {} 91 return 92 with open(path, 'rb') as index_file: 93 buf = index_file.read() 94 self.from_bytes(buf) 95 96 def save(self): 97 self._retry_wrapper(self._save_impl) 98 99 def _save_impl(self): 100 folder_path = _resolve_path_for_writing(self._folder_res_path) 101 if not os.path.exists(folder_path): 102 os.makedirs(folder_path) 103 path = _resolve_path_for_writing(self.index_res_path) 104 with open(path, 'wb') as index_file: 105 index_file.write(self.to_bytes()) 106 index_file.flush() 107 os.fsync(index_file.fileno()) 108 109 def update(self, mutator): 110 111 def _txn(): 112 self._load_impl() 113 mutator(self) 114 self._save_impl() 115 116 self._retry_wrapper(_txn)