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:
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 Réponses :
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
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)
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.
Vous voulez donc que
user_id + type
soit unique, sauf lorsquetype
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.