Skip to content

Commit 0233192

Browse files
authored
Fallback to FS cache when sqlite fails (#22201)
Fixes #22010 Fixes #21495 Fixes #21440 This handles 3 crash scenarios where we cannot use `sqlite` cache. I propose to try falling back to FS cache in case it might work. If it doesn't not work either (e.g. read-only filesystem), it already fails gracefully. Note that FS cache fails completely silently, but I think here it is better to write a short note, but still return 0 (assuming there are no type errors). (Not adding tests because I don't like mocking)
1 parent 9f56386 commit 0233192

1 file changed

Lines changed: 36 additions & 11 deletions

File tree

‎mypy/build.py‎

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,9 @@ def default_flush_errors(
385385
extra_plugins = extra_plugins or []
386386

387387
# Create metastore before workers to avoid race conditions.
388-
metastore = create_metastore(options, parallel_worker=False)
388+
metastore = create_metastore(options, parallel_worker=False, flush_errors=flush_errors)
389+
# In case we failed to initialize sqlite cache, pass this info to the parallel workers.
390+
options.sqlite_cache = isinstance(metastore, SqliteMetadataStore)
389391
workers = []
390392
connect_threads = []
391393
# A quasi-unique ID for this specific mypy invocation.
@@ -1896,17 +1898,40 @@ def exclude_from_backups(target_dir: str) -> None:
18961898
pass
18971899

18981900

1899-
def create_metastore(options: Options, parallel_worker: bool) -> MetadataStore:
1900-
"""Create the appropriate metadata store."""
1901+
def create_metastore(
1902+
options: Options,
1903+
parallel_worker: bool,
1904+
flush_errors: Callable[[str | None, list[str], bool], None] | None = None,
1905+
) -> MetadataStore:
1906+
"""Create the appropriate metadata store.
1907+
1908+
Note: there are few scenarios where we may fail to initialize sqlite cache,
1909+
for example read-only FS, or a Python build without sqlite support. In such
1910+
cases we print a note and fall back to FS cache.
1911+
"""
19011912
if options.sqlite_cache:
1902-
mds: MetadataStore = SqliteMetadataStore(
1903-
_cache_dir_prefix(options),
1904-
set_journal_mode=not parallel_worker,
1905-
num_shards=options.sqlite_num_shards,
1906-
)
1907-
else:
1908-
mds = FilesystemMetadataStore(_cache_dir_prefix(options))
1909-
return mds
1913+
try:
1914+
import sqlite3
1915+
1916+
try:
1917+
return SqliteMetadataStore(
1918+
_cache_dir_prefix(options),
1919+
set_journal_mode=not parallel_worker,
1920+
num_shards=options.sqlite_num_shards,
1921+
)
1922+
except (OSError, sqlite3.OperationalError) as os_error:
1923+
msg = f"Failed to initialize sqlite cache: {os_error}"
1924+
except ImportError as import_error:
1925+
msg = f"Cannot import sqlite: {import_error}"
1926+
if parallel_worker:
1927+
# If the sqlite cache fails *only* in the workers, this indicates something
1928+
# very wrong. It is tricky to fail gracefully, so we raise ValueError to make
1929+
# sure this exception is not swallowed in the worker top-level code.
1930+
raise ValueError(msg)
1931+
if flush_errors:
1932+
msg = f"mypy: note: {msg}"
1933+
flush_errors(None, [msg, "mypy: note: Using filesystem cache instead"], False)
1934+
return FilesystemMetadataStore(_cache_dir_prefix(options))
19101935

19111936

19121937
def get_meta_ex_name(meta_name: str) -> str:

0 commit comments

Comments
 (0)