Stamp FileSystemCache revalidations and entries with the monotonic clock (#100357)
### What?
`FileSystemCache` dates tag revalidations and cache entries with the
wall clock, but since #94457 `areTagsExpired` compares them against the
monotonic clock (`performance.timeOrigin + performance.now()`).
When the wall clock runs ahead, a revalidation is dated in the future,
so it's ignored and the old page keeps being served.
### Why?
I ran into this with `next start`, when our E2E tests kept getting stale
pages after `revalidateTag`. In a fresh 16.4.2 app with the wall clock
5ms ahead, 30 out of 30 reads right after a revalidation returned the
old page. With this change, it's 0 of 30.
The two clocks drift apart in ordinary setups, for example when NTP, a
VM or WSL2 corrects the wall clock while the server runs.
### How?
Use the monotonic clock everywhere that `FileSystemCache` dates
something, as `incremental-cache/index.ts` already does:
- `revalidateTag` and `set()` stamp with `performance.timeOrigin +
performance.now()` instead of `Date.now()`.
- `set()` also stores that timestamp on disk, as
`routeCacheLastModified` in the `.meta` file and as `lastModified` in
the fetch cache entry. Reads used to take the file's mtime(wall-clock
time).
Files without a stored time, from `next build` or an older Next.js
version, still fall back to the mtime. They were written before the
server started, so their mtime is earlier than any revalidation it
records.
This matters when entries are read from disk: with `cacheMaxMemorySize:
0`, or after an eviction from the memory cache. With only the in-memory
stamps fixed, a page written just before a revalidation comes back with
an mtime that's ahead, so it isn't even marked stale. Running `next
start` with the memory cache off and the wall clock stepped 1s ahead, 20
of 40 revalidations left the old page in place that way. With both
changes, it's 0 of 40.
Notes:
- `routeCacheLastModified` was only written when a build seed was
promoted. Runtime writes set it now, so the "preserves seed age" test
checks for it instead of its absence.
- Fetch cache files are kept across deploys, and they gain a
`lastModified` field.
- `test/production/app-dir/file-system-cache-clock` offsets
`performance.timeOrigin` at startup and checks the first request after
`revalidateTag` for an ISR page, an ISR route handler and a dynamic
page, with the default memory cache and `cacheMaxMemorySize: 0`. It also
restarts on a fetch cache file without `lastModified`. It fails on
canary, and with only the in-memory fix the `cacheMaxMemorySize: 0`
cases fail.
- Ran the new test with `test-start-webpack` and `test-start-turbo`, and
four existing revalidation and ISR suites with `test-start-webpack`. Not
run in dev.