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

【Bug】Memory 对象会导致 Rime 无法同步 #335

Closed
mirtlecn opened this issue May 5, 2024 · 20 comments
Closed

【Bug】Memory 对象会导致 Rime 无法同步 #335

mirtlecn opened this issue May 5, 2024 · 20 comments

Comments

@mirtlecn
Copy link

mirtlecn commented May 5, 2024

#330 所示

Memory 无法用于已经启用,且用户词典打开(默认情况)的方案,会导致大部分前端同步时抛出词典锁定的错误。

而如果想要使用 Memory 对象,所用方案很可能为启用用户词典状态(默认),lua 并未阻止此危险操作。所以此设计似有问题。请确认。

复现

测试方案(内含报错信息):

test_lua.zip

  • 引入一个使用 Memory 对象的 lua,重新部署,该 Memory 对象指向 luna_pinyin
  • 使用 luna_pinyin 打字,保证生成一些用户词
  • 同步,各个前端报错,提示用户词典被其他程序锁定,部分前端用户词典无法同步。

有关问题的前端:ibus-rime,Squirrelfcitx5-rime(不会显示错误,但无法继续同步)

预期情况

Memory 可以正常工作,不导致用户词典锁定。

或者以下情况均可:

  • 在新建 Memory 时,若检索到用户词典为 leveldb,则提示用户无法进行操作,中止操作,保证前端正常同步;
  • 另设一个函数,提供 Memory 所提供的检索固态词典的能力,不涉及用户词典
  • 修复 Memory 函数,当且仅当使用了其关涉用户词典的操作时,才去尝试打开 leveldb

问题 lua

-- filter:
--   - lua_fiter@*test

local F = {}

function F.init( env )
    -- F.foo = Memory( env.engine, env.engine.schema) -- error
    -- local foo = Memory( env.engine, env.engine.schema)  -- no error
    -- env.foo = Memory( env.engine, env.engine.schema) -- error
end

function F.func( input, env )
    -- F.foo = Memory( env.engine, env.engine.schema) -- error
    local foo = Memory( env.engine, env.engine.schema) -- error
    -- env.foo = Memory( env.engine, env.engine.schema) -- error

    for cand in input:iter() do
        cand.comment = '*' -- make sure this lua being called
        yield( cand )
    end
end

return F

Originally posted by @mirtlecn in #333 (comment)

@mirtlecn mirtlecn changed the title 如何实现这个需求: 如何实现这个需求:检索某个已经开启的方案的固态词典(table.bin,prism.bin),而不影响其他功能 May 5, 2024
@mirtlecn mirtlecn changed the title 如何实现这个需求:检索某个已经开启的方案的固态词典(table.bin,prism.bin),而不影响其他功能 【Bug】Memory 对象会导致 Rime 无法同步 May 5, 2024
@shewer
Copy link
Contributor

shewer commented May 5, 2024

檢查 整個方案中有無 leveldb 實例,且已 loaded
( 可以使用 :close() 關閉, 但可能再也開不起來了,因爲 user_dict 調用 db 是常開狀態 )

如果有和 方案中是否有衝突
上次討論中有使用 lua 環境製造 db_pool_ 的腳本 ,要取消
或是 用 :close() 全關閉.

確保 lua 環境中 所有 leveldb 實例中 與 user_dict 衝突

還是一個原則 儘量不要用 leveldb 開啓 user_dict 的db

@mirtlecn
Copy link
Author

mirtlecn commented May 5, 2024

fcitx5-rime 似乎不会将错误写入 log,可能需要一些额外的操作。因此我选了 ibus-rime 测试

不知道你能否复现?

rime/home 有一个 issue 看 log 似乎也与此相关:

我觉得它是 bug 的原因是:

  • 若这种 Memory 的使用方式有误,其造成的错误不应该在同步的时候才抛出,应当在新建 Memory 的时候就直接中止操作
  • 检索固态词典,必须新建一个 Memory 对象,且无须访问 leveldb,当前的行为或许可以避免或优化

@shewer
Copy link
Contributor

shewer commented May 5, 2024

你可以先將 更新的 expent_translator.lua 重新下載
試試
commits --> commit 變數名 錯誤
這個錯誤 只會造成 callback 呼叫 失敗 沒有無法開啓userdict 的問題

@mirtlecn
Copy link
Author

mirtlecn commented May 5, 2024

我没有用那个 lua,整个方案只用了一个测试 lua,就是上面写的那个。

@mirtlecn
Copy link
Author

mirtlecn commented May 5, 2024

test_lua.zip

@shewer

这个错误在 rime_api_console 看不出来,其他前端均能复现。

Edit:我将部分补充更新到了主 issue 说明,请确认。

@mirtlecn
Copy link
Author

mirtlecn commented May 6, 2024

如若诸位维护者有空,请确认此 issue 是否存在,或者给予指导。

个人已经在两台虚拟机的 fcitx5-rime 和 ibus-rime 稳定复现,且鼠须管、fcitx5 android 也有相关报错。rime/home 的一则 issue 据我判断也可能与此有关。

简易的测试方案和 lau 已经在主贴中提供。

@hchunhui @TsinamLeung (为当初引入该对象的贡献者)

感激不尽。

原因是此问题会影响多个前端,导致前端无法继续同步,且报错不指向 lua 错误而是指向用户词典被锁定。

@TsinamLeung
Copy link
Contributor

如若诸位维护者有空,请确认此 issue 是否存在,或者给予指导。

个人已经在两台虚拟机的 fcitx5-rime 和 ibus-rime 稳定复现,且鼠须管、fcitx5 android 也有相关报错。rime/home 的一则 issue 据我判断也可能与此有关。

简易的测试方案和 lau 已经在主贴中提供。

@hchunhui @TsinamLeung (为当初引入该对象的贡献者)

感激不尽。

原因是此问题会影响多个前端,导致前端无法继续同步,且报错不指向 lua 错误而是指向用户词典被锁定。

我目前工作電腦還沒有測試還境,因此我見到你的Code我猜想是: 如果Memory沒有被回收則會佔用leveldb。
透過觀察前端的implementation,可以見到是rime_api的sync_user_data作全部同步操作的

RIME_API Bool RimeSyncUserData() {
  RimeCleanupAllSessions();
  Deployer& deployer(Service::instance().deployer());
  deployer.ScheduleTask("installation_update");
  deployer.ScheduleTask("backup_config_files");
  deployer.ScheduleTask("user_dict_sync");
  return Bool(deployer.StartMaintenance());
}

RIME_API void RimeCleanupAllSessions() {
  Service::instance().CleanupAllSessions();
}
void Service::CleanupAllSessions() {
  sessions_.clear();
}
// SessionMap session_
//  using SessionMap = map<SessionId, an<Session>>;

此步似乎是notify to release session.(這個操作應把Memory全部清空)
我認為短期解決方案是:
在每次F.func 中新建Memory實例,確保在function結束後能被Release(可能會導致效能下降)

之後可能需要lua component能夠捕獲到session deconstructing時的訊息,並傳遞給各Lua部件,調起sessionDestory的handler(需要用戶自行編寫)
這樣做的目的是因為,luaComponent是隨着librime加載而initialize的,僅當librime 關閉時,lua 的 context才會清空。因此需要自行管理自librime取得的組件

@mirtlecn
Copy link
Author

mirtlecn commented May 7, 2024

在每次F.func 中新建Memory實例,確保在function結束後能被Release

感谢指导,我会测试并给予反馈

我已经测试并更新到主 issue,在 func 函数中使用 local foo = Memory 新建一个对象也会报错。

唯一不报错的,是在 init 函数中,用 local foo = Memory 新建一个对象。

@shewer
Copy link
Contributor

shewer commented May 7, 2024

在關閉 section 時 lua component 也會解構 LuaObj env_ 也會移除
可以試試 在 LuaMemory LuaTranslator .... 有 Lua member
解構時 加上lua_->gc();

所有 關於 librime 物件 都要掛在 env 中 ,不能掛到 upvalue or global

--module file
local F={}
local m_mem
g_mem=nil
function F.init(env)
   env.mem = Memory(.....)
    F.mem = env.mem  -- NG
    m_mem = env.mem  -- NG
    g_mem = env.mem -- NG
end
function F.func(inp, env)
   local mem = env.mem   -- ok
end

@mirtlecn
Copy link
Author

mirtlecn commented May 7, 2024

在關閉 section 時 lua component 也會解構 LuaObj env_ 也會移除
可以試試 在 LuaMemory LuaTranslator .... 有 Lua member
解構時 加上lua_->gc();

所有 關於 librime 物件 都要掛在 env 中 ,不能掛到 upvalue or global

--module file
local F={}
local m_mem
g_mem=nil
function F.init(env)
   env.mem = Memory(.....)
    F.mem = env.mem  -- NG
    m_mem = env.mem  -- NG
    g_mem = env.mem -- NG
end
function F.func(inp, env)
   local mem = env.mem   -- ok
end
-- filter:
--   - lua_fiter@*test

local F = {}

function F.init( env )
    -- local foo = Memory( env.engine, env.engine.schema)  -- no error
    -- env.foo = Memory( env.engine, env.engine.schema) -- error
end

function F.func( input, env )
    local foo = Memory( env.engine, env.engine.schema) -- error
    -- env.foo = Memory( env.engine, env.engine.schema) -- error

    for cand in input:iter() do
        cand.comment = '*' -- make sure this lua being called
        yield( cand )
    end
end

return F

@shewer
Copy link
Contributor

shewer commented May 7, 2024

加上個 試試,在同步時 蜀發 engine 解構 lua_component 完成 fini 後 gc()
Memory Component.Translator Processor .....
要掛在 env
git apply patch

diff --git a/src/lua_gears.cc b/src/lua_gears.cc
index 9e1f704..0f8a01e 100644
--- a/src/lua_gears.cc
+++ b/src/lua_gears.cc
@@ -159,6 +159,7 @@ LuaFilter::~LuaFilter() {
       LOG(ERROR) << "LuaFilter::~LuaFilter of "<< name_space_ << " error(" << e.status << "): " << e.e;
     }
   }
+  lua_->gc();
 }

 //--- LuaTranslator
@@ -186,6 +187,7 @@ LuaTranslator::~LuaTranslator() {
       LOG(ERROR) << "LuaTranslator::~LuaTranslator of "<< name_space_ << " error(" << e.status << "): " << e.e;
     }
   }
+  lua_->gc();
 }

 //--- LuaSegmentor
@@ -213,6 +215,7 @@ LuaSegmentor::~LuaSegmentor() {
       LOG(ERROR) << "LuaSegmentor::~LuaSegmentor of "<< name_space_ << " error(" << e.status << "): " << e.e;
     }
   }
+  lua_->gc();
 }

 //--- LuaProcessor
@@ -244,6 +247,7 @@ LuaProcessor::~LuaProcessor() {
       LOG(ERROR) << "LuaProcessor::~LuaProcessor of "<< name_space_ << " error(" << e.status << "): " << e.e;
     }
   }
+  lua_->gc();
 }

 }  // namespace rime

@shewer
Copy link
Contributor

shewer commented May 7, 2024

在關閉 section 時 lua component 也會解構 LuaObj env_ 也會移除
可以試試 在 LuaMemory LuaTranslator .... 有 Lua member
解構時 加上lua_->gc();
所有 關於 librime 物件 都要掛在 env 中 ,不能掛到 upvalue or global

--module file
local F={}
local m_mem
g_mem=nil
function F.init(env)
   env.mem = Memory(.....)
    F.mem = env.mem  -- NG
    m_mem = env.mem  -- NG
    g_mem = env.mem -- NG
end
function F.func(inp, env)
   local mem = env.mem   -- ok
end
-- filter:
--   - lua_fiter@*test

local F = {}

function F.init( env )
    -- local foo = Memory( env.engine, env.engine.schema)  -- no error
    -- env.foo = Memory( env.engine, env.engine.schema) -- error
end

function F.func( input, env )
    local foo = Memory( env.engine, env.engine.schema) -- error
    -- env.foo = Memory( env.engine, env.engine.schema) -- error

    for cand in input:iter() do
        cand.comment = '*' -- make sure this lua being called
        yield( cand )
    end
end

return F

Memory 中有 OnCommit(ctx) 掛在 translation 中 怪怪的

function F.fini(env)
   env.mem=nil
   collectgarbage('collect')
end

@mirtlecn
Copy link
Author

mirtlecn commented May 7, 2024

不好意思,改源码然后编译测试,我暂时没这个条件,虚拟机里面编译 boost 和 librime 很慢,会卡死。

主机是 Weasel,倒是能编译,但它不报这个错误。

function F.fini(env)
   env.mem=nil
   collectgarbage('collect')
end

这个我试试,之后反馈给你。不过我认为没有帮助,因为你看

在 func 函数内用 local foo = memory 的方式仍然会引起 leveldb 被锁定,不像是因为没有回收资源造成的错误

@shewer
Copy link
Contributor

shewer commented May 7, 2024

lifetime env 底下的 一定大於 func 下的 local var

在 func 中 local mem 參考 env.mem 執行完 回收 mem
func(inp,env)
local mem = env.mem
end -- distory mem [point to env.mem]

@mirtlecn
Copy link
Author

mirtlecn commented May 7, 2024

lifetime env 底下的 一定大於 func 下的 local var

在 func 中 local mem 參考 env.mem 執行完 回收 mem func(inp,env) local mem = env.mem end -- distory mem [point to env.mem]

赋值 nil 并 collectgarbage('collect') 不会导致同步错误!

当前测试了 ibus-rime 是成功解决了问题。其他的还没测试,

@shewer

local F = {}

function F.init( env )
    env.mem = Memory( env.engine, env.engine.schema)
end

function F.func( input, env )
    for cand in input:iter() do
        cand.comment = '-' -- test if this lua has been called
        yield( cand )
    end
end

function F.fini(env)
   env.mem=nil
   collectgarbage('collect')
end

return F

@mirtlecn
Copy link
Author

mirtlecn commented May 8, 2024

fcitx5-rime 测试也没有问题。

这样做了后,直接 update_user_dict 也不会有问题了。

想问下,我想保持对 librime 之前版本的兼容,希望直接在 fini 函数里面,手动回收 Memory,如之前的代码所示,应当没有负面效果吧?此操作不会在用户输入的情况下因回收资源而造成卡顿吧?

@shewer
Copy link
Contributor

shewer commented May 8, 2024

影響不大 , 只發生在 session close ()
還有更頻繁的 LuaTranslation

@hchunhui
Copy link
Owner

依赖gc释放内存以外的资源并不是很合理,因为gc的时机本来就是不确定的。最好是能给 Memory 加一个 close()release() 之类的方法,在 fini() 里面手动确保释放掉它打开的词典。

@shewer
Copy link
Contributor

shewer commented May 12, 2024

根據上述狀況 ComponentReg 可能也有問題 , table_translator script_translator 也是屬於 Memory 類

Memory 成員有 字典(unique_ptr) 和 notifier 要如何解構 自己?
env.mem:close() ?

原來的想法是 env (LuaObj) 在消滅時 env 下面的 userdata
就可以被回收了,所以在此時觸發一次 gc()

@TsinamLeung
Copy link
Contributor

TsinamLeung commented May 12, 2024 via email

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

4 participants