2
votes

Résolution des conflits dans Tarantool (comment réparer la réplication en mode maître-maître en cas de conflits)

Comment puis-je implémenter la résolution des conflits lorsque j'utilise Tarantool dans un scénario multi-maître?

Je développe un service qui devrait être hautement disponible, j'ai donc décidé d'utiliser nginx comme équilibreur de charge (avec directive de sauvegarde) pour deux nœuds de tarantool (avec option lecture seule désactivée). Il réessaye les demandes échouées à un autre nœud, mais en cas de problèmes de réseau (par exemple, entre les nœuds de tarantool), des conflits peuvent survenir.

Comment puis-je implémenter l'un des scénarios suivants:

  1. Choisissez un tuple plus récent sur chaque nœud
  2. Logique personnalisée (peut être un autre espace pour les conflics, etc.)

Une autre question est de savoir comment définir un index composé Nullable unique (null est une valeur qui peut apparaître plusieurs fois)

id - PK
user_id + type - unique nullable tree index (type is nullable)
user_id has non unique tree index

Index:

| id | user_id | type | {some data} |


3 commentaires

Vous voulez donc que user_id + type soit unique, sauf lorsque type est nul?


@DarkWiiPlayer, très proche. J'ai posé la question ici: stackoverflow.com/ questions / 56737313 /…


Ce serait difficile à réaliser; si possible, vous devriez envisager d'utiliser une configuration maître-esclave à la place ou simplement créer vos interactions de base de données sans conflit.


3 Réponses :


2
votes

1) Vous avez besoin d'un déclencheur before_replace sur l'espace qui peut être en conflit pour implémenter les règles de résolution des conflits de votre application.

https: // www.tarantool.io/en/doc/2.1/book/box/box_space/#box-space-before-replace

Dans le déclencheur, vous pouvez comparer l'ancien et le nouvel enregistrement de réplique et choisir celui à utiliser (ou ignorer complètement la mise à jour ou fusionner deux enregistrements ensemble).

2) Vous devez définir le déclencheur au bon moment, avant que l'espace ne commence à recevoir des mises à jour. La façon dont vous définissez généralement le déclencheur before_replace est correcte lorsque l'espace est créé, vous avez donc besoin d'un déclencheur définissant un autre déclencheur sur l'espace système _space, pour capturer le moment où votre espace est créé et y définir le déclencheur. Cela peut être le déclencheur on_replace, https : //www.tarantool.io/en/doc/2.1/book/box/box_space/#box-space-on-replace la différence entre before_replace et on_replace < / em> est que * on_replace est appelé après l'insertion d'une ligne dans l'espace, et before_replace est appelé avant. 3) Pour définir le déclencheur _space: on_replace (), vous avez également besoin du bon timing. Le meilleur moment à utiliser est lorsque _space vient d'être créé, qui est le déclencheur box.ctl.on_schema_init (). https: // www .tarantool.io / fr / doc / 2.1 / book / box / box_ctl / # lua-function.box.ctl.on_schema_init


0 commentaires

3
votes

Concernant le deuxième point de la réponse de Kostja (combinaison de on_ctl_init + _space: on_replace), il y a une autre astuce: vous devrez utiliser box.on_commit pour accéder à l'espace en cours de création. L'extrait résultant serait le suivant:

local my_space_name = 'ny_space'
local my_trigger = function(old, new) ... end
box.schema.on_schema_init(function()
    box.space._space:on_replace(function(_, new_space)
        if new_space.name == my_space_name then
            box.on_commit(function()
                box.space[my_space_name]:before_replace(my_trigger)
            end
        end
    end)
end)


0 commentaires

0
votes

Concernant 2) j'ai rencontré le problème. L'activation du déclencheur "juste au moment de la création de l'espace" dans mon cas provoque des erreurs en lecture seule. La raison est la suivante: le déclencheur essaie de passer à la table de statistiques lors de la lecture de WAL.

local my_space_name = 'myspace'
local function loading_before_replace(old, new)
    if box.info.status == "loading" then
        return
    end
    box.space.my_space_name:before_replace(before_replace, loading_before_replace)
    return before_replace(old,new)
end

local function _space_on_replace(old, new)  
    if not new or not new.name then
        return
    end
    -- skip system spaces
    if string.startswith(new.name, "_") then
        return
    end

    if new.name == my_space_name  then
        box.on_commit(function()
            box.space.my_space_name:before_replace(loading_before_replace)
        end)
    end
end

local function set_triggers()
    box.ctl.on_schema_init(function()
        box.space._space:on_replace(_space_on_replace)
    end)
end

Dans ce cas, je dois activer le déclencheur seulement alors que WAL a été lu. Et avant que la synchronisation de réplication ne démarre (sinon je peux frapper une collision ou une statistique lâche). J'ai trouvé ceci est le bon moment pour le faire. J'active un déclencheur après que box.info.status a changé de "chargement". Comme ceci:

local function before_replace(old, new)
    -- collision resolving here
    if box.session.type() ~= 'applier' then
        box.space.stat:upsert(
            { "key", 0 },
            {
                {"+", stat.COUNT, 1}
            })
    end
    return
end

Donc, le déclencheur before_replace () sera exécuté et activé lors du premier engagement dans myspace après l'initiale Lecture WAL.

Peut-être qu'il est possible d'appuyer sur le déclencheur sur box.info.status changé? Cela pourrait rendre le code plus propre. Mais je ne sais pas si c'est possible.


0 commentaires