• Andres Freund's avatar
    Move page initialization from RelationAddExtraBlocks() to use, take 2. · 0d1fe9f7
    Andres Freund authored
    Previously we initialized pages when bulk extending in
    RelationAddExtraBlocks(). That has a major disadvantage: It ties
    RelationAddExtraBlocks() to heap, as other types of storage are likely
    to need different amounts of special space, have different amount of
    free space (previously determined by PageGetHeapFreeSpace()).
    
    That we're relying on initializing pages, but not WAL logging the
    initialization, also means the risk for getting
    "WARNING:  relation \"%s\" page %u is uninitialized --- fixing"
    style warnings in vacuums after crashes/immediate shutdowns, is
    considerably higher. The warning sounds much more serious than what
    they are.
    
    Fix those two issues together by not initializing pages in
    RelationAddExtraPages() (but continue to do so in
    RelationGetBufferForTuple(), which is linked much more closely to
    heap), and accepting uninitialized pages as normal in
    vacuumlazy.c. When vacuumlazy encounters an empty page it now adds it
    to the FSM, but does nothing else.  We chose to not issue a debug
    message, much less a warning in that case - it seems rarely useful,
    and quite likely to scare people unnecessarily.
    
    For now empty pages aren't added to the VM, because standbys would not
    re-discover such pages after a promotion. In contrast to other sources
    for empty pages, there's no corresponding WAL records triggering FSM
    updates during replay.
    
    Previously when extending the relation, there was a moment between
    extending the relation, and acquiring an exclusive lock on the new
    page, in which another backend could lock the page. To avoid new
    content being put on that new page, vacuumlazy needed to acquire the
    extension lock for a brief moment when encountering a new page. A
    second corner case, only working somewhat by accident, was that
    RelationGetBufferForTuple() sometimes checks the last page in a
    relation for free space, without consulting the FSM; that only worked
    because PageGetHeapFreeSpace() interprets the zero page header in a
    new page as no free space.  The lack of handling this properly
    required reverting the previous attempt in 68420054.
    
    This issue can be solved by using RBM_ZERO_AND_LOCK when extending the
    relation, thereby avoiding this window. There's some added complexity
    when RelationGetBufferForTuple() is called with another buffer (for
    updates), to avoid deadlocks, but that's rarely hit at runtime.
    
    Author: Andres Freund
    Reviewed-By: Tom Lane
    Discussion: https://postgr.es/m/20181219083945.6khtgm36mivonhva@alap3.anarazel.de
    0d1fe9f7
hio.c 22.5 KB