Stale chunks were the thing I'd check first. If a page shrinks from 10 chunks to 7, the vectors for chunks numbered 7 through 9 just sit in the store and keep getting retrieved. I mean hashing the content doesn't really fix that by itself, so i'd run a delete on source_url before every upsert. Or a generation key, if you don't want to delete in place
And keep fetched_at in the metadata, otherwise you've got no idea how old the stuff you're serving actually is