Skip to content

MDB-43767: consider db in crash recovery state as db in non-terminal state#132

Merged
munakoiso merged 4 commits into
yandex:mainfrom
munakoiso:MDB-43767
Mar 25, 2026
Merged

MDB-43767: consider db in crash recovery state as db in non-terminal state#132
munakoiso merged 4 commits into
yandex:mainfrom
munakoiso:MDB-43767

Conversation

@munakoiso

Copy link
Copy Markdown
Collaborator

No description provided.

@munakoiso munakoiso requested a review from a team as a code owner March 20, 2026 08:56
@munakoiso

Copy link
Copy Markdown
Collaborator Author

@aak74

aak74 commented Mar 24, 2026

Copy link
Copy Markdown
Collaborator

Race condition

Race condition in _rewind_from_source(): pg_rewind may start before PostgreSQL fully stops

In _rewind_from_source() /src/main.py:1271 at line 1288-1293, after calling stop_postgresql(), the code immediately proceeds to do_rewind() without waiting for PostgreSQL to actually shut down. This can cause pg_rewind to fail with "target server must be shut down cleanly" error.

Suggested Fix

Remove is_postgresql_dead parameter and always verify PostgreSQL is fully stopped before running pg_rewind:

def _rewind_from_source(self, limit, new_primary):  # removed is_postgresql_dead
    logging.info("Starting pg_rewind")
    
    # Wait for source database to be available
    if not helpers.await_for(
        lambda: not self._check_host_is_really_dead(new_primary),
        limit,
        'source database alive and ready for rewind',
    ):
        return None
    
    # Mark destructive operation in ZK
    if not self.zk.write('%s/%s/op' % (self.zk.MEMBERS_PATH, helpers.get_hostname()), 'rewind', need_lock=False):
        logging.error('Unable to save destructive op state: rewind')
        return None
    
    self.db.pgpooler('stop')
    
    # Always ensure PostgreSQL is stopped before rewind
    if self.db.get_postgresql_status() == 0:  # if process is running
        if self.stop_postgresql(timeout=limit) != 0:
            logging.error('Could not stop PostgreSQL. Will retry.')
            return None
        
        # Wait for PostgreSQL to fully stop
        if not helpers.await_for(
            lambda: self.db.get_postgresql_status() != 0,
            limit,
            'PostgreSQL fully stopped'
        ):
            logging.error('PostgreSQL did not stop in time. Will retry.')
            return None
    
    self.checks['rewind'] += 1
    if self.db.do_rewind(new_primary) != 0:
        logging.error('Error while using pg_rewind. Will retry.')
        return True
    
    # Rewind has finished successfully so we can drop its operation node
    self.zk.delete('%s/%s/op' % (self.zk.MEMBERS_PATH, helpers.get_hostname()))
    return self._attach_to_primary(new_primary, limit)

@aak74

aak74 commented Mar 24, 2026

Copy link
Copy Markdown
Collaborator

Race condition in _rewind_from_source(): pg_rewind may start before PostgreSQL fully stops

Added here: #138

@aak74 aak74 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@munakoiso munakoiso merged commit 2b46db5 into yandex:main Mar 25, 2026
32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants