起因

前段时间想给 openresty 开源做点贡献,就去找一些 lua-resty-xxxtodo list 实践,顺便也可以看下项目本身的源码,这样就能带着目的去看源码了。于是顺手找到了 lua-resty-lrucache 的工程,这个工程我们之前一直在使用,它是用于 worker 内部的 key-value 缓存,并且采用简单的 lru 策略进行淘汰。

具体任务

add new method flush_all for flushing out everything in the cache.

曲折历程

首先还是需要先看懂源码。该工程的实现包括设计了两个双向循环队列,分布用来表示 cache_queuefree_queue,并且设计了key2node 以及 node2key 两个 hash 表,用来查找相应的 node 或者 key。由于当删除一个 node 的时候,对于 lua 的实现来说只是简单的 key2node[key] = nil 以及 node2key[node] = nil 的操作,而不是真正意义上的物理释放内存,这样一来,频繁的淘汰删除操作会导致内存增大,于是作者后来又设计了针对这个问题的 ffi 实现。不管是纯 lua 实现还是 ffi 的实现,总体的设计都是一致的。

由于作者之前已经实现了删除一个 node 的操作,对于 flush_all 的实现,我自然会采用一个 while 循环,然后从 cache_queue 中循环取出 node 并删除,然后加入 free_queue 列表。这其中包括修复了一些格式问题,这说明提交 patch 的代码需要严格规范。

后来另一个开发者觉得我只实现了纯 lua 的版本,于是在这的基础上帮我实现了 ffi 版本上的 flush_all 并且提交了相应的 case,我把它的代码也就顺便合并到了我的 patch 中。

原本以为可以顺利的合并进主分支了,结果一位大牛提出了一个将 O(n) 改进到 O(1) 的建议,这还真是我之前没仔细想的,直接将 cache_queue 链接到 free_queue 上。在改的时候我发现之前对两个队列的理解还不是很到位导致改的不对,经过多次查看源码我才真正理解作者的设计理念。