LS: thread-safe cache read/write through locking

This commit is contained in:
Michael Panchenko committed 2025-06-09 18:07:24 +02:00
1 parent 9202924189
commit ce0c82869b
1 file changed
+33 -25
+33 -25
View File
@@ -252,6 +252,7 @@ class LanguageServer:
self.open_file_buffers: Dict[str, LSPFileBuffer] = {}
# --------------------------------- MODIFICATIONS BY ORAIOS ---------------------------------
self._cache_lock = threading.Lock()
self._document_symbols_cache: dict[str, Tuple[str, Tuple[List[multilspy_types.UnifiedSymbolInformation], List[multilspy_types.UnifiedSymbolInformation]]]] = {}
"""Maps file paths to a tuple of (file_content_hash, result_of_request_document_symbols)"""
self.load_cache()
@@ -853,16 +854,17 @@ class LanguageServer:
# Should be fixed in the future, it's a small performance optimization
cache_key = f"{relative_file_path}-{include_body}"
with self.open_file(relative_file_path) as file_data:
file_hash_and_result = self._document_symbols_cache.get(cache_key)
if file_hash_and_result is not None:
file_hash, result = file_hash_and_result
if file_hash == file_data.content_hash:
self.logger.log(f"Returning cached document symbols for {relative_file_path}", logging.DEBUG)
return result
with self._cache_lock:
file_hash_and_result = self._document_symbols_cache.get(cache_key)
if file_hash_and_result is not None:
file_hash, result = file_hash_and_result
if file_hash == file_data.content_hash:
self.logger.log(f"Returning cached document symbols for {relative_file_path}", logging.DEBUG)
return result
else:
self.logger.log(f"Content for {relative_file_path} has changed. Will overwrite in-memory cache", logging.DEBUG)
else:
self.logger.log(f"Content for {relative_file_path} has changed. Will overwrite in-memory cache", logging.DEBUG)
else:
self.logger.log(f"No cache hit for symbols with {include_body=} in {relative_file_path}", logging.DEBUG)
self.logger.log(f"No cache hit for symbols with {include_body=} in {relative_file_path}", logging.DEBUG)
self.logger.log(f"Requesting document symbols for {relative_file_path} from the Language Server", logging.DEBUG)
response = await self.server.send.document_symbol(
@@ -947,8 +949,9 @@ class LanguageServer:
result = flat_all_symbol_list, root_nodes
self.logger.log(f"Caching document symbols for {relative_file_path}", logging.DEBUG)
self._document_symbols_cache[cache_key] = (file_data.content_hash, result)
self._cache_has_changed = True
with self._cache_lock:
self._document_symbols_cache[cache_key] = (file_data.content_hash, result)
self._cache_has_changed = True
return result
async def request_full_symbol_tree(self, within_relative_path: str | None = None, include_body: bool = False) -> List[multilspy_types.UnifiedSymbolInformation]:
@@ -1603,35 +1606,40 @@ class LanguageServer:
return Path(self.repository_root_path) / ".serena" / "cache" / "document_symbols_cache_v20-05-25.pkl"
def save_cache(self):
if self._cache_has_changed:
with self._cache_lock:
if not self._cache_has_changed:
self.logger.log("No changes to document symbols cache, skipping save", logging.DEBUG)
return
self.logger.log(f"Saving updated document symbols cache to {self._cache_path}", logging.INFO)
self._cache_path.parent.mkdir(parents=True, exist_ok=True)
try:
with open(self._cache_path, "wb") as f:
pickle.dump(self._document_symbols_cache, f)
self._cache_has_changed = False
except Exception as e:
self.logger.log(
f"Failed to save document symbols cache to {self._cache_path}: {e}. "
"Note: this may have resulted in a corrupted cache file.", logging.ERROR
)
else:
self.logger.log(f"No changes to document symbols cache, skipping save", logging.DEBUG)
self._cache_has_changed = False
f"Failed to save document symbols cache to {self._cache_path}: {e}. "
"Note: this may have resulted in a corrupted cache file.",
logging.ERROR,
)
def load_cache(self):
if not self._cache_path.exists():
return
self.logger.log(f"Loading document symbols cache from {self._cache_path}", logging.INFO)
with open(self._cache_path, "rb") as f:
with self._cache_lock:
self.logger.log(f"Loading document symbols cache from {self._cache_path}", logging.INFO)
try:
self._document_symbols_cache = pickle.load(f)
with open(self._cache_path, "rb") as f:
self._document_symbols_cache = pickle.load(f)
except Exception as e:
# cache often becomes corrupt, so just skip loading it
self.logger.log(
f"Failed to load document symbols cache from {self._cache_path}: {e}. Possible cause: the cache file is corrupted. "
"Check for any errors related to saving the cache in the logs.",
logging.ERROR
)
f"Failed to load document symbols cache from {self._cache_path}: {e}. Possible cause: the cache file is corrupted. "
"Check for any errors related to saving the cache in the logs.",
logging.ERROR,
)
async def request_workspace_symbol(self, query: str) -> Union[List[multilspy_types.UnifiedSymbolInformation], None]: