Skip to content

fix(sync): handle 'already exists' race condition in try_create#127

Open
berlikm wants to merge 1 commit into
OpensourceICTSolutions:developmentfrom
berlikm:fix/sync-already-exists-race
Open

fix(sync): handle 'already exists' race condition in try_create#127
berlikm wants to merge 1 commit into
OpensourceICTSolutions:developmentfrom
berlikm:fix/sync-already-exists-race

Conversation

@berlikm

@berlikm berlikm commented Jul 15, 2026

Copy link
Copy Markdown

Summary

When multiple RQ workers sync devices concurrently, two jobs can both pass (returns empty), then both call for the same hostgroup/template/tag. Zabbix rejects the second with "already exists". This propagates as a RuntimeError and aborts the entire host sync — including the HostSync itself, leaving a host with no interfaces, no templates, and no tags.

Root cause

ZabbixSyncBase.try_create() calls api_object().create() without catching the case where the object was created by a concurrent job in the gap between find_by_name() and create().

Fix

Catch the "already exists" error in try_create() and retry via find_by_name() to recover the existing ID:

def try_create(self):
    try:
        result = self.api_object().create(**self.get_create_params())
        return result.get(self.result_key(), [None])[0]
    except Exception as err:
        if 'already exists' in str(err).lower():
            found_by_name = self.find_by_name()
            if len(found_by_name) == 1:
                return found_by_name[0].get(self.id_field.split('.')[-1])
        msg = f'{self.__class__.__name__} creation failed: {err}'
        raise RuntimeError(msg)

Testing

Verified on dev NetBox + Zabbix 7.0.28 with 173 concurrent device/VM sync jobs (HU + JP sites). Before the fix, HostGroupSync failures cascaded to HostSync, leaving hosts with zero interfaces. After the fix, all concurrent creates resolve cleanly — 0 failures from race conditions.

The fix is generic — it applies to all sync classes (HostGroupSync, TemplateSync, ProxySync, etc.) since it lives in the base class.

When multiple RQ workers sync devices concurrently, two jobs can
both pass find_by_name() (returns empty), then both try to create
the same hostgroup/template/tag. Zabbix rejects the second with
"already exists". Catch that error and retry via find_by_name()
instead of propagating the RuntimeError.
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.

1 participant