Leonard Volarić Horvat
03/04/2025, 9:06 PMmrt 04 16:24:00 myhost sudo[841]: cur.execute("INSERT INTO history (cache_key, run_id) VALUES (?, ?)", (cache_key, run_id))
mrt 04 16:24:00 myhost sudo[841]: sqlite3.OperationalError: attempt to write a readonly database
Unfortunately, I don't have access to this machine right now. This happened at a client, and a colleague lifted the logs for later inspection. I will have access to the machine later this week, but I would like some hypotheses on what went wrong. I would first look at simply the disk being clogged up, but we've put Hamilton's cache in /dev/shm/hamilton_cache, which does not reside on disk, AFAIK. And the RAM equivalent of this problem would likely result in the OOM killer killing... well, something noticeable. But since this worked perfectly fine for months, I can't help but suspect a system-wide issue. But I might be wrong. Any suggestions on where to look?Leonard Volarić Horvat
03/04/2025, 9:07 PMmrt 04 16:24:00 myhost sudo[841]: ********************************************************************************
mrt 04 16:24:00 myhost sudo[841]: Traceback (most recent call last):
mrt 04 16:24:00 myhost sudo[841]: File "/home/myuser/.local/share/virtualenvs/backend-84hNa8eQ/lib/python3.8/site-packages/hamilton/execution/graph_functions.py", line 294, in execute_lifecycle_for_node
mrt 04 16:24:00 myhost sudo[841]: result = __adapter.call_lifecycle_method_sync(
mrt 04 16:24:00 myhost sudo[841]: File "/home/myuser/.local/share/virtualenvs/backend-84hNa8eQ/lib/python3.8/site-packages/hamilton/lifecycle/base.py", line 955, in call_lifecycle_method_sync
mrt 04 16:24:00 myhost sudo[841]: return getattr(adapter, method_name)(**kwargs)
mrt 04 16:24:00 myhost sudo[841]: File "/home/myuser/.local/share/virtualenvs/backend-84hNa8eQ/lib/python3.8/site-packages/hamilton/caching/adapter.py", line 1377, in do_node_execute
mrt 04 16:24:00 myhost sudo[841]: self._set_stored_metadata(
mrt 04 16:24:00 myhost sudo[841]: File "/home/myuser/.local/share/virtualenvs/backend-84hNa8eQ/lib/python3.8/site-packages/hamilton/caching/adapter.py", line 763, in _set_stored_metadata
mrt 04 16:24:00 myhost sudo[841]: self.metadata_store.set(
mrt 04 16:24:00 myhost sudo[841]: File "/home/myuser/.local/share/virtualenvs/backend-84hNa8eQ/lib/python3.8/site-packages/hamilton/caching/stores/sqlite.py", line 147, in set
mrt 04 16:24:00 myhost sudo[841]: cur.execute("INSERT INTO history (cache_key, run_id) VALUES (?, ?)", (cache_key, run_id))
mrt 04 16:24:00 myhost sudo[841]: sqlite3.OperationalError: attempt to write a readonly database
mrt 04 16:24:00 myhost sudo[841]: ERROR:hamilton.driver:-------------------------------------------------------------------
mrt 04 16:24:00 myhost sudo[841]: Oh no an error! Need help with Hamilton?Thierry Jean
03/04/2025, 9:10 PMsqlite3, the default backend for the metadata_store, which stores metadata about what node was executed, what code version, what data version, etc.
The readonly error has to do with how sqlite handles multiple connections.Thierry Jean
03/04/2025, 9:11 PMThierry Jean
03/04/2025, 9:14 PMnode A executes, read/write what it needs, then node B does its thing, then node C. These operations are very quick
If you're running multiple DAG in parallel against the same cache, they could collide. If your cache grows really big, the metadata read operation required by the cache logic could become slower/more expensive and increase the likelihood of this errorThierry Jean
03/04/2025, 9:15 PMmetadata_store is ?Leonard Volarić Horvat
03/04/2025, 9:15 PMLeonard Volarić Horvat
03/04/2025, 9:16 PMmetadata_store on that machine - which I cannot do right nowLeonard Volarić Horvat
03/04/2025, 9:17 PMLeonard Volarić Horvat
03/04/2025, 9:19 PMThierry Jean
03/04/2025, 9:22 PMmetadata_store with SQLite, it should be a single file and be easy to inspect.
For something more robust, you could help build a Postgres backend (it shouldn't be too difficult if you're following the open source sqlite)
or you could use the in memory cache for each of your parallel process and use some handwritten logic to load_from / `persist_to`; Your parallel processes will benefit from other's work only when they sync thoughLeonard Volarić Horvat
03/04/2025, 9:31 PMyou could help build a Postgres backendYou mean, build a Postgres backend for Hamilton's caching store?
or you could use the in memory cache for each of your parallel processIndeed, having separate caches for each process seems like the cleanest approach.
Leonard Volarić Horvat
03/04/2025, 9:33 PM/dev/shm/, so it already is in-memory, it's just not using your built-in InMemoryMetadataStore etc. Is there a fundamental difference between instantiating that class vs manually specifying a tmpfs path? Is there some significant logic there that we're leaving on the table?Thierry Jean
03/04/2025, 9:40 PMmetadata_store writes metadata to an SQLite database; on the next execution, you can retrieve metadata from disk
• the main limitation is having multiple DAG execution accessing the same cache concurrently.
• if you must have concurrent access, Postgres could be a good option because it supports concurrent connections.
• the InMemoryMetadataStore doesn't write data to SQLite / to disk; this is useful in notebook or interactive python or if you loop over Driver.execute() in a single script. After the process terminates, the metadata disappears if you're not persisting it
So far I was assuming that you were running the "same" dataflow in parallel. If you're running different dataflows, you can set the cache path such that each process writes to a different SQLite database. This should entirely prevent the error your encountered
dr = Builder().with_modules(...).with_cache(path="/dev/shm/process1").build()Louwrens
03/04/2025, 9:58 PMLeonard Volarić Horvat
03/04/2025, 9:59 PMLouwrens
03/04/2025, 10:00 PMLeonard Volarić Horvat
03/04/2025, 10:00 PM