Skip to content

Commit

Permalink
Fix server socket collisions
Browse files Browse the repository at this point in the history
Now, by default the server socket file is located in the server working
directory which is unique.

If the server belongs to a replica set, then the server socket path will
be also unique since the server socket file is located in the working
directory of the replica set which is unique as well.

Resolves #297
  • Loading branch information
ochaplashkin authored and ylobankov committed Apr 11, 2023
1 parent 8523e5c commit 930b63b
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 13 deletions.
8 changes: 7 additions & 1 deletion luatest/replica_set.lua
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ function ReplicaSet:initialize()
self.alias = 'rs'
self.id = ('%s-%s'):format(self.alias, utils.generate_id())
self.workdir = fio.pathjoin(self._server.vardir, self.id)
fio.mktree(self.workdir)

if self.servers then
local configs = table.deepcopy(self.servers)
Expand All @@ -82,14 +83,19 @@ end
function ReplicaSet:build_server(config)
checks('table', self._server.constructor_checks)
if config then config = table.deepcopy(config) end
return self._server:new(config, {rs_id = self.id})
return self._server:new(config, {rs_id = self.id, vardir = self.workdir})
end

--- Add the server object to the replica set.
-- The added server object should be built via the `ReplicaSet:build_server`
-- function.
--
-- @tab server Server object to be added to the replica set.
function ReplicaSet:add_server(server)
checks('table', 'table')
if not server.rs_id then
error('Server should be built via `ReplicaSet:build_server` function')
end
if self:get_server(server.alias) then
error(('Server with alias "%s" already exists in replica set')
:format(server.alias))
Expand Down
13 changes: 8 additions & 5 deletions luatest/server.lua
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,7 @@ function Server:initialize()
end

if self.net_box_uri == nil and self.net_box_port == nil then
self.net_box_uri = self.build_listen_uri(self.alias)
fio.mktree(self.vardir)
self.net_box_uri = self.build_listen_uri(self.alias, self.rs_id or self.id)
end
if self.net_box_uri == nil and self.net_box_port then
self.net_box_uri = 'localhost:' .. self.net_box_port
Expand All @@ -151,6 +150,7 @@ function Server:initialize()
error(('Net box URI must be <= max Unix domain socket path length (%d chars)')
:format(max_unix_socket_path[system]))
end
fio.mktree(fio.dirname(self.net_box_uri))
end

self.env = utils.merge(self.env or {}, self:build_env())
Expand Down Expand Up @@ -220,13 +220,16 @@ function Server:build_env()
return res
end

--- Build a listen URI based on the given server alias.
--- Build a listen URI based on the given server alias and extra path.
-- The resulting URI: `<Server.vardir>/[<extra_path>/]<server_alias>.sock`.
-- Provide a unique alias or extra path to avoid collisions with other sockets.
-- For now, only UNIX sockets are supported.
--
-- @string server_alias Server alias.
-- @string[opt] extra_path Extra path relative to the `Server.vardir` directory.
-- @return string
function Server.build_listen_uri(server_alias)
return fio.pathjoin(Server.vardir, server_alias .. '.sock')
function Server.build_listen_uri(server_alias, extra_path)
return fio.pathjoin(Server.vardir, extra_path or '', server_alias .. '.sock')
end

--- Start a server.
Expand Down
8 changes: 4 additions & 4 deletions test/collect_rs_artifacts_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ local g = t.group()
local Server = t.Server

local function build_specific_replica_set(alias_suffix)
local rs = ReplicaSet:new()
local box_cfg = {
replication_timeout = 0.1,
replication_connect_timeout = 1,
Expand All @@ -22,11 +23,10 @@ local function build_specific_replica_set(alias_suffix)
table.deepcopy(box_cfg),
{
replication ={
Server.build_listen_uri(s1_alias),
Server.build_listen_uri(s2_alias),
Server.build_listen_uri(s3_alias)
Server.build_listen_uri(s1_alias, rs.id),
Server.build_listen_uri(s2_alias, rs.id),
Server.build_listen_uri(s3_alias, rs.id)
}})
local rs = ReplicaSet:new()

rs:build_and_add_server({alias = s1_alias, box_cfg = box_cfg})
rs:build_and_add_server({alias = s2_alias, box_cfg = box_cfg})
Expand Down
52 changes: 52 additions & 0 deletions test/replica_set_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,55 @@ g.test_remove_rs_artifacts_when_test_success = function()

t.assert_equals(fio.path.exists(g.rs.workdir), false)
end

g.before_test('test_rs_no_socket_collision_with_custom_alias', function()
g.rs = ReplicaSet:new()
end)

g.test_rs_no_socket_collision_with_custom_alias = function()
local s1 = g.rs:build_server({alias = 'foo'})
local s2 = g.rs:build_server({alias = 'bar'})

t.assert(s1.vardir:find(g.rs.id, 1, true))
t.assert(s2.vardir:find(g.rs.id, 1, true))
t.assert_equals(s1.net_box_uri, ('%s/foo.sock'):format(s1.vardir))
t.assert_equals(s2.net_box_uri, ('%s/bar.sock'):format(s2.vardir))
end

g.after_test('test_rs_no_socket_collision_with_custom_alias', function()
g.rs:drop()
end)

g.before_test('test_rs_custom_properties_are_not_overridden', function()
g.rs = ReplicaSet:new()
end)

g.test_rs_custom_properties_are_not_overridden = function()
local socket = ('%s/custom.sock'):format(Server.vardir)
local workdir = ('%s/custom'):format(Server.vardir)

local s = g.rs:build_server({net_box_uri = socket, workdir=workdir})

t.assert_equals(s.net_box_uri, socket)
t.assert_equals(s.workdir, workdir)
end

g.after_test('test_rs_custom_properties_are_not_overridden', function()
g.rs:drop()
end)

g.before_test('test_rs_raise_error_when_add_custom_server', function()
g.rs = ReplicaSet:new()
end)

g.test_rs_raise_error_when_add_custom_server = function()
local s = Server:new()

t.assert_error_msg_contains(
'Server should be built via `ReplicaSet:build_server` function',
function() g.rs:add_server(s) end)
end

g.after_test('test_rs_raise_error_when_add_custom_server', function()
g.rs:drop()
end)
59 changes: 56 additions & 3 deletions test/server_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ g.test_net_box = function()
t.assert_equals(server.net_box.state, 'active')

server:eval('function f(x,y) return {x, y} end;')
t.assert_equals(server:call('f', {1,'test'}), {1, 'test'})
t.assert_equals(server:call('f', {1, 'test'}), {1, 'test'})

server.net_box:close()
t.assert_error_msg_equals('Connection closed', server.eval, server, '')
Expand Down Expand Up @@ -346,8 +346,9 @@ end

g.test_save_server_artifacts_when_test_failed = function()
local s1 = Server:new() -- empty config
local s2 = Server:new({
workdir = ('%s/%s'):format(Server.vardir, os.tmpname())}) -- workdir passed
local s2 = Server:new(
{workdir = ('%s/%s'):format(Server.vardir, os.tmpname())}
) -- workdir passed

s1:start()
s2:start()
Expand Down Expand Up @@ -376,3 +377,55 @@ g.test_remove_server_artifacts_when_test_success = function()

t.assert_equals(fio.path.exists(s.workdir), false)
end

g.test_server_build_listen_uri = function()
local uri = Server.build_listen_uri('foo')
t.assert_equals(uri, ('%s/foo.sock'):format(Server.vardir))

local uri_extra = Server.build_listen_uri('foo', 'bar')
t.assert_equals(uri_extra, ('%s/bar/foo.sock'):format(Server.vardir))
end

g.before_test('test_no_socket_collision_with_default_alias', function()
g.s1 = Server:new()
g.s2 = Server:new()

g.s1:start()
g.s2:start()
end)

g.test_no_socket_collision_with_default_alias = function()
g.s1:exec(function() rawset(_G, 'foo', 'foo-value') end)
local foo = g.s2:exec(function() rawget(_G, 'foo') end)

t.assert_equals(foo, nil)
t.assert_not_equals(g.s1.net_box_uri, g.s2.net_box_uri)
end

g.after_test('test_no_socket_collision_with_default_alias', function()
g.s1:drop()
g.s2:drop()
end)

g.test_no_socket_collision_with_duplicate_alias = function()
g.s1 = Server:new({alias = 'foo'})
g.s2 = Server:new({alias = 'foo'})

t.assert_not_equals(g.s1.net_box_uri, g.s2.net_box_uri)
end

g.after_test('test_no_socket_collision_with_duplicate_alias', function()
g.s1:drop()
g.s2:drop()
end)

g.test_netbox_uri_is_not_overridden = function()
local socket = ('%s/my-custom.sock'):format(Server.vardir)
g.s1 = Server:new({net_box_uri = socket})

t.assert_equals(g.s1.net_box_uri, socket)
end

g.after_test('test_netbox_uri_is_not_overridden', function()
g.s1:drop()
end)

0 comments on commit 930b63b

Please sign in to comment.