Skip to content

Commit dcb099e

Browse files
committed
shared-state: multiwriter use counter based merge strategy
Replace the timestamp merge strategy that has many issues on the field with a counter + a random number merge strategy.
1 parent 1e50aa4 commit dcb099e

2 files changed

Lines changed: 56 additions & 10 deletions

File tree

packages/shared-state/files/usr/lib/lua/shared-state.lua

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -301,14 +301,16 @@ function SharedStateMultiWriter:new(dataType, logger)
301301
return newInstance
302302
end
303303

304+
304305
function SharedStateMultiWriter:_merge(stateSlice)
305-
-- Make merge based on timestamp
306+
--! Make merge based on an incremental counter (changes) and a random number (fortune)
306307
local stateSlice = stateSlice or {}
307308
for key,rv in pairs(stateSlice) do
308309
local lv = self.storage[key]
309-
if ( lv == nil or lv.lastModified < rv.lastModified ) then
310+
if ( lv == nil or lv.changes < rv.changes or
311+
( lv.changes == rv.changes and lv.fortune < rv.fortune )) then
310312
self.log( "debug", "Updating entry for: "..key.." older: "..
311-
(lv and lv.lastModified or 'no entry') .." newer: "..rv.lastModified )
313+
(lv and lv.changes or 'no entry') .." newer: "..rv.changes )
312314
self.storage[key] = rv
313315
self.changed = true
314316
end
@@ -324,11 +326,18 @@ function SharedStateMultiWriter:insert(data)
324326
self:notifyHooks()
325327
end
326328

329+
function shared_state._getFortune()
330+
return math.random(1, 100000)
331+
end
332+
327333
function SharedStateMultiWriter:_insert(key, data)
328334
local lv = self.storage[key]
329335
if (lv == nil or not utils.deepcompare(lv.data, data)) then
336+
local changes = lv and lv.changes + 1 or 0
330337
self.storage[key] = {
331338
lastModified=os.time(),
339+
changes=changes,
340+
fortune=shared_state._getFortune(),
332341
author=io.input("/proc/sys/kernel/hostname"):read("*line"),
333342
data=data
334343
}

packages/shared-state/tests/test_shared_state.lua

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,50 @@ describe('Shared State MultiWriter tests #sharedstatemultiwriter', function()
126126
after_each('', function()
127127
test_utils.teardown_test_dir()
128128
end)
129-
130-
it('test merge data from remote db with newer timestamp', function()
129+
130+
it('test insert new data', function()
131+
local ss = shared_state.SharedStateMultiWriter:new('foo')
132+
local sharedStateB = shared_state.SharedStateMultiWriter:new('B')
133+
stub(shared_state, "_getFortune", function () return 100 end)
134+
ss:insert({ bar = '123'})
135+
local db = ss:get()
136+
assert.is.equal('123', db.bar.data)
137+
assert.is.equal(0, db.bar.changes)
138+
assert.is.equal(100, db.bar.fortune)
139+
end)
140+
141+
it('test merge data from remote db with more changes (newer)', function()
142+
local sharedStateA = shared_state.SharedStateMultiWriter:new('A')
143+
local sharedStateB = shared_state.SharedStateMultiWriter:new('B')
144+
sharedStateA:insert({ bar = 'foo', baz = 'qux', zig = 'old_zag'})
145+
sharedStateB:insert({ zig = 'new_zag'})
146+
sharedStateB:insert({ zig = 'newer_zag'})
147+
sharedStateA:merge(sharedStateB:get())
148+
local dbA = sharedStateA:get()
149+
assert.is.equal('foo', dbA.bar.data)
150+
assert.is.equal('qux', dbA.baz.data)
151+
assert.is.equal('newer_zag', dbA.zig.data)
152+
end)
153+
154+
it('test merge data from remote db with less changes (older)', function()
155+
local sharedStateA = shared_state.SharedStateMultiWriter:new('A')
156+
local sharedStateB = shared_state.SharedStateMultiWriter:new('B')
157+
sharedStateA:insert({ bar = 'foo', baz = 'qux', zig = 'zag'})
158+
sharedStateA:insert({ zig = 'new_zag'})
159+
sharedStateB:insert({ zig = 'old_zag'})
160+
sharedStateA:merge(sharedStateB:get())
161+
local dbA = sharedStateA:get()
162+
assert.is.equal('foo', dbA.bar.data)
163+
assert.is.equal('qux', dbA.baz.data)
164+
assert.is.equal('new_zag', dbA.zig.data)
165+
end)
166+
167+
it('test merge data from remote db with same changes but more luck', function()
131168
local sharedStateA = shared_state.SharedStateMultiWriter:new('A')
132169
local sharedStateB = shared_state.SharedStateMultiWriter:new('B')
133-
stub(os, "time", function () return 1616095000 end)
170+
stub(shared_state, "_getFortune", function () return 100 end)
134171
sharedStateA:insert({ bar = 'foo', baz = 'qux', zig = 'old_zag'})
135-
stub(os, "time", function () return 1616096000 end)
172+
stub(shared_state, "_getFortune", function () return 1000 end)
136173
sharedStateB:insert({ zig = 'new_zag'})
137174
sharedStateA:merge(sharedStateB:get())
138175
local dbA = sharedStateA:get()
@@ -141,12 +178,12 @@ describe('Shared State MultiWriter tests #sharedstatemultiwriter', function()
141178
assert.is.equal('new_zag', dbA.zig.data)
142179
end)
143180

144-
it('test merge data from remote db with older timestamp', function()
181+
it('test merge data from remote db with same changes but less luck', function()
145182
local sharedStateA = shared_state.SharedStateMultiWriter:new('A')
146183
local sharedStateB = shared_state.SharedStateMultiWriter:new('B')
147-
stub(os, "time", function () return 1616096000 end)
184+
stub(shared_state, "_getFortune", function () return 1000 end)
148185
sharedStateA:insert({ bar = 'foo', baz = 'qux', zig = 'new_zag'})
149-
stub(os, "time", function () return 1616095000 end)
186+
stub(shared_state, "_getFortune", function () return 100 end)
150187
sharedStateB:insert({ zig = 'old_zag'})
151188
sharedStateA:merge(sharedStateB:get())
152189
local dbA = sharedStateA:get()

0 commit comments

Comments
 (0)