Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sync timer 唯一性问题 #1

Open
fankeke opened this issue Dec 21, 2017 · 15 comments
Open

sync timer 唯一性问题 #1

fankeke opened this issue Dec 21, 2017 · 15 comments
Assignees

Comments

@fankeke
Copy link

fankeke commented Dec 21, 2017

https://github.com/upyun/lua-resty-sync/blob/master/lib/resty/sync.lua#L324

if var and var ~= id then
release_lock(lock)
return true
end

没有看具体逻辑,但感觉这里应该要释放lock(抢到后)

@tokers
Copy link
Member

tokers commented Dec 21, 2017

这里不加不会影响到功能,但是从代码严谨的角度来说是要加。欢迎提交 PR。

@fankeke
Copy link
Author

fankeke commented Dec 21, 2017

@tokers 另外,感觉用lock来做唯一性的timer没有那么优雅,可以用worker id来做。
在初始化每个sync的时候,可以random(worker的数量)一个值赋值给sync,然后该sync就可以由这个random值出来的id所对应的worker来同步。这个也避免了LOCK_KEY和LOCK_TIME_KEY的使用。

(另外os.time的精度不够,连续起两个sync会导致同样的LOCK_TIME_KEY也是一个问题)

如果可行的话,我提个PR

@tokers
Copy link
Member

tokers commented Dec 21, 2017

@fankeke 你的方案,如果持有 sync timer 的 worker 进程 crash 了,怎么保证新起来的 worker 可以重新创建出 sync timer 来?

@fankeke
Copy link
Author

fankeke commented Dec 21, 2017

新的worker再走一遍init_worker阶段 ,也能够创建。

function _M.new(...)
.....
local sync = {
....
worker_id = random(ngx.worker.count())
}
.....
}

function _M.start(self)
.....
local worker_id = self.worker_id
if worker_id ~= ngx.worker.id() then
return
end
.......
end

基本思想和上面一样

@tokers
Copy link
Member

tokers commented Dec 21, 2017

那这个 worker_id 应该要放到共享内存里。

@tokers
Copy link
Member

tokers commented Dec 21, 2017

从你的伪代码来看,持有 sync timer 的 worker 如果挂了,新起来一个 worker,也是随机一个 worker_id,进入到 start 函数,如果这个随机出来的 worker_id 不等于 ngx.worker.id() 的值的话,此时全局就没有 sync 定时器了。

@fankeke
Copy link
Author

fankeke commented Dec 21, 2017

额,好像是的 。。。 思路有点问题。

@tokers tokers self-assigned this Dec 21, 2017
@tokers
Copy link
Member

tokers commented Dec 21, 2017

@fankeke 不过 os.time 的问题,可能是会有精度不够的问题,可以在这个基础上结合一个随机数,哈哈

@fankeke
Copy link
Author

fankeke commented Dec 21, 2017

嗯,我提个PR可以

@fankeke
Copy link
Author

fankeke commented Dec 22, 2017

@tokers 当前的做法还是有些问题:
场景如下:如果有两个worker,2个sync,sync1和sync2
nginx启动时,其中sync1和sync2都被worker1持有,
然后worker2突然挂掉,然后重启执行init_worker阶段,然后继续创建sync1和sync2. (因为此时的lock_key和lock_time_key都已经不同了,而且没有其他worker进行抢锁,必然会创建所有的sync)

这个时候就会发生问题, 是不是这样?

@tokers
Copy link
Member

tokers commented Dec 22, 2017

@fankeke 是的。
目前 resty-sync 的做法能够完全避免掉这些问题。

@fankeke
Copy link
Author

fankeke commented Dec 22, 2017

@tokers 目前的resty-sync如何避免我上面的场景的?

@tokers
Copy link
Member

tokers commented Dec 22, 2017

@fankeke 你可以仔细看下 _M.start 这个函数,我通过在共享内存里设置一个特殊的标记,即用 LOCK_TIMER_KEY 作为 key,存放成功启动 sync timer 的 worker 的 id,过期时间为这个 sync 实例所设置的 interval + 10,且每次定时器在处理回调的时候,都会更新下这个 key ,以避免过期。

如果某个 worker 进入到这个函数,先第一次抢锁,抢到锁的,首先会判断这个 LOCK_TIMER_KEY 对应的值是不是存在,如果不存在,则由这个 worker 抢占到 timer,同时共享内存里的 LOCK_TIMER_KEY 存放它的 id。

如果这个 LOCK_TIMER_KEY 对应的值已经是存在的,需要判断其值和当前 worker 自己的 id 是否一致,不一致则立刻返回,一致则接着往下走,抢占到 timer。

这样子,在有 worker 崩溃的时候,如果不是 timer owner 崩溃,则由于 LOCK_TIMER_KEY 对应的值和它本身的 ID 不同,是不会创建出 timer 来的,相反,如果是之前的 timer owner 奔溃,由于 worker id 只能是 [0, n - 1],新起来的 worker 必然和刚刚奔溃的 worker id 一致,此时它便可以重新创建出 timer 来,保证 resty.sync 工作正常。

@fankeke
Copy link
Author

fankeke commented Dec 22, 2017

@tokers crash后的worker创建的 LOCK_TIMER_KEY 在变化,并不是不变的,这个你考虑了吗?

这是我起了4个worker,创建一个sync时的shdict的数据,我kill了三次worker后的数据:
sync_timer_1513914468:1
sync_timer_1513914438:0
sync_timer_1513915510:2
_time_ex1:1513916022
_data_ex1:16
_version_ex1:version 10

此时3个worker都持有了这个sync的timer。

@tokers
Copy link
Member

tokers commented Dec 22, 2017

@fankeke 恩,这个 new 操作你要放到 init 阶段,可以放在 init 阶段再试试

@tokers tokers changed the title 这里是不是要有unlock? sync timer 唯一性问题 Dec 23, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants