From 77fcbcd81062ad762dfd48033458e22396b54edc Mon Sep 17 00:00:00 2001 From: Igor Zolotarev Date: Thu, 19 Dec 2024 20:06:18 +0300 Subject: [PATCH 01/11] Add `mark_left` function --- CHANGELOG.md | 5 +++++ membership.lua | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a92de19..bc3875b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Added + +- `mark_left` function to mark removed members as `left`. + ## [2.4.5] - 2024-06-24 ### Fixed @@ -13,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Invalid events parsing. ## [2.4.4] - 2024-04-09 + ### Fixed - Invalid payload parsing in anti entropy step. diff --git a/membership.lua b/membership.lua index 9a00507..14fa0de 100755 --- a/membership.lua +++ b/membership.lua @@ -736,6 +736,39 @@ local function leave() return true end +--- Forcefully send leave message about an instance. +-- @function mark_left +-- @treturn boolean +-- `true` if call succeeds, +-- `false` if member has already left. +local function mark_left(uri_to_leave) + if _sock == nil then + return false + end + + -- Perform artificial events.generate() and instantly send it + local myself = members.get(uri_to_leave) + if not myself then + return false + end + local event = events.pack({ + uri = uri_to_leave, + status = opts.LEFT, + incarnation = myself.incarnation, + ttl = members.count(), + }) + local msg_msgpacked = msgpack.encode({uri_to_leave, 'LEAVE', msgpack.NULL, {event}}) + local msg_encrypted = opts.encrypt(msg_msgpacked) + for _, uri in ipairs(members.filter_excluding('unhealthy', uri_to_leave)) do + local addr = resolve(uri) + if addr then + _sock:sendto(addr.host, addr.port, msg_encrypted) + end + end + + return true +end + --- Member data structure. -- A member is represented by the table with the following fields: -- @@ -984,6 +1017,7 @@ end return { init = init, leave = leave, + mark_left = mark_left, members = get_members, broadcast = broadcast, pairs = function() return pairs(get_members()) end, From 2b6b86d1f66d452b2cd212211435cce7b9469cba Mon Sep 17 00:00:00 2001 From: Igor Zolotarev Date: Tue, 24 Dec 2024 19:54:31 +0300 Subject: [PATCH 02/11] Add test --- test/test_init.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/test_init.py b/test/test_init.py index d6f32d1..f1084c2 100644 --- a/test/test_init.py +++ b/test/test_init.py @@ -19,6 +19,20 @@ def test_death(servers, helpers): helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'alive']) helpers.wait_for(servers[13302].check_status, ['localhost:13301', 'alive']) +def test_mark_left(servers, helpers): + servers[13302].kill() + helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'suspect']) + helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'dead']) + + assert servers[13301].conn.eval('return membership.mark_left("localhost:13302")')[0] + helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'left']) + + servers[13302].start() + helpers.wait_for(servers[13302].connect) + helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'alive']) + helpers.wait_for(servers[13302].check_status, ['localhost:13301', 'alive']) + + def test_reinit(servers, helpers): assert servers[13301].add_member('localhost:13302') From e1fc7881cf09cd78fb1c5b72ecdfc291d9dac90d Mon Sep 17 00:00:00 2001 From: Igor Zolotarev Date: Tue, 24 Dec 2024 19:57:02 +0300 Subject: [PATCH 03/11] fix blank lines --- test/test_init.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_init.py b/test/test_init.py index f1084c2..860d78f 100644 --- a/test/test_init.py +++ b/test/test_init.py @@ -19,6 +19,7 @@ def test_death(servers, helpers): helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'alive']) helpers.wait_for(servers[13302].check_status, ['localhost:13301', 'alive']) + def test_mark_left(servers, helpers): servers[13302].kill() helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'suspect']) @@ -33,7 +34,6 @@ def test_mark_left(servers, helpers): helpers.wait_for(servers[13302].check_status, ['localhost:13301', 'alive']) - def test_reinit(servers, helpers): assert servers[13301].add_member('localhost:13302') helpers.wait_for(servers[13302].check_status, ['localhost:13301', 'alive']) From 7a28cb21290fc90d76630d17e8c5c9e9101ad6f1 Mon Sep 17 00:00:00 2001 From: Igor Zolotarev Date: Tue, 24 Dec 2024 20:00:55 +0300 Subject: [PATCH 04/11] Reinit instance --- test/test_init.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_init.py b/test/test_init.py index 860d78f..132e8e4 100644 --- a/test/test_init.py +++ b/test/test_init.py @@ -29,6 +29,8 @@ def test_mark_left(servers, helpers): helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'left']) servers[13302].start() + assert servers[13301].conn.eval("return membership.init('localhost', 13302)")[0] + helpers.wait_for(servers[13302].connect) helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'alive']) helpers.wait_for(servers[13302].check_status, ['localhost:13301', 'alive']) From 400e620a8babd56796d8c11f844972375819a996 Mon Sep 17 00:00:00 2001 From: Igor Zolotarev Date: Tue, 24 Dec 2024 20:06:32 +0300 Subject: [PATCH 05/11] try again --- test/test_init.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_init.py b/test/test_init.py index 132e8e4..aff4208 100644 --- a/test/test_init.py +++ b/test/test_init.py @@ -29,9 +29,9 @@ def test_mark_left(servers, helpers): helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'left']) servers[13302].start() + helpers.wait_for(servers[13302].connect) assert servers[13301].conn.eval("return membership.init('localhost', 13302)")[0] - helpers.wait_for(servers[13302].connect) helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'alive']) helpers.wait_for(servers[13302].check_status, ['localhost:13301', 'alive']) From 5e33f4a552fc2a60771a1fae545fd3b7698e31c4 Mon Sep 17 00:00:00 2001 From: Igor Zolotarev Date: Sun, 12 Jan 2025 04:26:46 +0300 Subject: [PATCH 06/11] try to add conditions to the other test --- test/test_init.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/test/test_init.py b/test/test_init.py index aff4208..a5d20a1 100644 --- a/test/test_init.py +++ b/test/test_init.py @@ -14,24 +14,11 @@ def test_death(servers, helpers): helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'suspect']) helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'dead']) - servers[13302].start() - helpers.wait_for(servers[13302].connect) - helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'alive']) - helpers.wait_for(servers[13302].check_status, ['localhost:13301', 'alive']) - - -def test_mark_left(servers, helpers): - servers[13302].kill() - helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'suspect']) - helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'dead']) - assert servers[13301].conn.eval('return membership.mark_left("localhost:13302")')[0] helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'left']) servers[13302].start() helpers.wait_for(servers[13302].connect) - assert servers[13301].conn.eval("return membership.init('localhost', 13302)")[0] - helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'alive']) helpers.wait_for(servers[13302].check_status, ['localhost:13301', 'alive']) From f6ef3f0767df5f6199da8238da16ba4998768e6a Mon Sep 17 00:00:00 2001 From: Igor Zolotarev Date: Sun, 12 Jan 2025 19:15:17 +0300 Subject: [PATCH 07/11] init --- test/test_init.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_init.py b/test/test_init.py index a5d20a1..d9442b1 100644 --- a/test/test_init.py +++ b/test/test_init.py @@ -19,6 +19,7 @@ def test_death(servers, helpers): servers[13302].start() helpers.wait_for(servers[13302].connect) + assert servers[13301].conn.eval("return membership.init('localhost', 13302)")[0] helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'alive']) helpers.wait_for(servers[13302].check_status, ['localhost:13301', 'alive']) From dbbeb620b7717346a14ab022974f85a09cb73b84 Mon Sep 17 00:00:00 2001 From: Igor Zolotarev Date: Sun, 12 Jan 2025 19:22:19 +0300 Subject: [PATCH 08/11] Add new test --- test/test_init.py | 4 ---- test/test_quit.py | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_init.py b/test/test_init.py index d9442b1..d6f32d1 100644 --- a/test/test_init.py +++ b/test/test_init.py @@ -14,12 +14,8 @@ def test_death(servers, helpers): helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'suspect']) helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'dead']) - assert servers[13301].conn.eval('return membership.mark_left("localhost:13302")')[0] - helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'left']) - servers[13302].start() helpers.wait_for(servers[13302].connect) - assert servers[13301].conn.eval("return membership.init('localhost', 13302)")[0] helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'alive']) helpers.wait_for(servers[13302].check_status, ['localhost:13301', 'alive']) diff --git a/test/test_quit.py b/test/test_quit.py index caffac7..36c094c 100644 --- a/test/test_quit.py +++ b/test/test_quit.py @@ -19,3 +19,7 @@ def test_rejoin(servers, helpers): assert servers[13302].conn.eval('return membership.init("localhost", 13302)')[0] assert servers[13301].add_member('localhost:13302') helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'alive']) + +def test_mark_left(servers, helpers): + assert servers[13301].conn.eval('return membership.mark_left("localhost:13302")')[0] + helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'left']) From 54041d6c7d6d6a0a5b8fd18646b69c244a582d57 Mon Sep 17 00:00:00 2001 From: Igor Zolotarev Date: Sun, 12 Jan 2025 19:25:06 +0300 Subject: [PATCH 09/11] fix blanks --- test/test_quit.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_quit.py b/test/test_quit.py index 36c094c..1a495f0 100644 --- a/test/test_quit.py +++ b/test/test_quit.py @@ -20,6 +20,7 @@ def test_rejoin(servers, helpers): assert servers[13301].add_member('localhost:13302') helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'alive']) + def test_mark_left(servers, helpers): assert servers[13301].conn.eval('return membership.mark_left("localhost:13302")')[0] helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'left']) From ba7d98dbc208df9e1b9dcf8e3ca3a6f80a2b0b02 Mon Sep 17 00:00:00 2001 From: Igor Zolotarev Date: Sun, 12 Jan 2025 19:28:48 +0300 Subject: [PATCH 10/11] check that instance is alive --- test/test_quit.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_quit.py b/test/test_quit.py index 1a495f0..2b5af12 100644 --- a/test/test_quit.py +++ b/test/test_quit.py @@ -22,5 +22,6 @@ def test_rejoin(servers, helpers): def test_mark_left(servers, helpers): + helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'alive']) assert servers[13301].conn.eval('return membership.mark_left("localhost:13302")')[0] helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'left']) From 62b2602d660303b752943979e176c158b7cc7c2e Mon Sep 17 00:00:00 2001 From: Igor Zolotarev Date: Mon, 13 Jan 2025 14:20:25 +0300 Subject: [PATCH 11/11] Add another condition --- membership.lua | 2 +- test/test_quit.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/membership.lua b/membership.lua index 14fa0de..9b31d9b 100755 --- a/membership.lua +++ b/membership.lua @@ -748,7 +748,7 @@ local function mark_left(uri_to_leave) -- Perform artificial events.generate() and instantly send it local myself = members.get(uri_to_leave) - if not myself then + if not myself or myself.status == opts.LEFT then return false end local event = events.pack({ diff --git a/test/test_quit.py b/test/test_quit.py index 2b5af12..54a7161 100644 --- a/test/test_quit.py +++ b/test/test_quit.py @@ -25,3 +25,9 @@ def test_mark_left(servers, helpers): helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'alive']) assert servers[13301].conn.eval('return membership.mark_left("localhost:13302")')[0] helpers.wait_for(servers[13301].check_status, ['localhost:13302', 'left']) + + # already has left + assert not servers[13301].conn.eval('return membership.mark_left("localhost:13302")')[0] + + # there are no such member + assert not servers[13301].conn.eval('return membership.mark_left("localhost:10000")')[0]