Skip to content

Commit 5666c91

Browse files
Use WAL with SQLite cache, fix close (#21154)
This is the more modern way to manage concurrency with SQLite. Relevant to current discussion, it means concurrent mypy runs using the cache will wait for each other, rather than fail SQLite also claims this is significantly faster, but I haven't yet done a good profile (If you are profiling this, note that WAL is a persistent setting, so you will want to delete the cache). This might also allow removing the `PRAGMA synchronous=OFF` Finally, I also explicitly close the connection in main. This is relevant to this change, because it forces checkpointing of the WAL, which keeps reads fast, reduces disk space and means the cache.db remains a single self-contained file under regular use Fixes #21136, see also discussion in #13916 For what it's worth, I feel there are many legitimate uses of concurrent mypy. At work, we often share cache between multiple projects. At home, I often end up having parallel runs with a debugger while working on mypy (although this PR just makes those ones hang waiting for the lock lol) --------- Co-authored-by: Ivan Levkivskyi <levkivskyi@gmail.com>
1 parent 949ccd0 commit 5666c91

File tree

2 files changed

+14
-3
lines changed

2 files changed

+14
-3
lines changed

mypy/main.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,14 +189,20 @@ def main(
189189
list([res]) # noqa: C410
190190

191191

192+
class BuildResultThunk:
193+
# We pass this around so that we avoid freeing memory, which is slow
194+
def __init__(self, build_result: build.BuildResult | None) -> None:
195+
self._result = build_result
196+
197+
192198
def run_build(
193199
sources: list[BuildSource],
194200
options: Options,
195201
fscache: FileSystemCache,
196202
t0: float,
197203
stdout: TextIO,
198204
stderr: TextIO,
199-
) -> tuple[build.BuildResult | None, list[str], bool]:
205+
) -> tuple[BuildResultThunk | None, list[str], bool]:
200206
formatter = util.FancyFormatter(
201207
stdout, stderr, options.hide_error_codes, hide_success=bool(options.output)
202208
)
@@ -227,8 +233,12 @@ def flush_errors(filename: str | None, new_messages: list[str], serious: bool) -
227233
blockers = True
228234
if not e.use_stdout:
229235
serious = True
236+
237+
if res:
238+
res.manager.metastore.close()
239+
230240
maybe_write_junit_xml(time.time() - t0, serious, messages, messages_by_file, options)
231-
return res, messages, blockers
241+
return BuildResultThunk(res), messages, blockers
232242

233243

234244
def show_messages(

mypy/metastore.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ def connect_db(db_file: str) -> sqlite3.Connection:
162162
# but without this flag, commits are *very* slow, especially when using HDDs,
163163
# see https://www.sqlite.org/faq.html#q19 for details.
164164
db.execute("PRAGMA synchronous=OFF")
165+
db.execute("PRAGMA journal_mode=WAL")
165166
db.executescript(SCHEMA)
166167
return db
167168

@@ -171,8 +172,8 @@ def __init__(self, cache_dir_prefix: str) -> None:
171172
# We check startswith instead of equality because the version
172173
# will have already been appended by the time the cache dir is
173174
# passed here.
175+
self.db = None
174176
if cache_dir_prefix.startswith(os.devnull):
175-
self.db = None
176177
return
177178

178179
os.makedirs(cache_dir_prefix, exist_ok=True)

0 commit comments

Comments
 (0)