I blame Microsoft for that one too, but I am open to changes.
We largely don't care what Microsoft did, as long as we can support all the features required to implement ID3D12ShaderCacheSession. I.e., generally the question should be "What makes sense?", rather than "What does Windows do?". That's slightly different from traditional Wine development, yes.
Microsoft's cache seems to flush on every write, at least for application-created caches. I don't want to do that in a blocking manner because depending on the hardware doing a disk write and possibly fsync is potentially worse than compiling spir-v.
That doesn't have to be a problem per se, but it does imply users of the API would e.g. need to spawn a separate thread to avoid blocking, yes.
The default behavior should probably be something more reliable than "flush only on close". Either flush after every n writes, have a worker thread flush after a time of inactivity (although I don't like the idea of spawning a thread), flush from an _onexit() callback.
A worker thread wouldn't seem inappropriate to me. It gives the advantage that writes are quick for the application (because they don't immediately hit the disk) and can be pooled in batches (which is quite faster for the database, because it only syncs at the end of the transaction).
Note though that if we introduce such a thread on the vkd3d-shader side of the API (as opposed to the user side), that would imply a thread per opened cache with the current API design.