Closed sci-42ver closed 2 months ago
我在 Weasel 不久之前也碰到过这个 error。在某个时间点后突然消失了。你提到了 issue:
https://github.com/rime/weasel/issues/199
我可以确定,这个问题在 15.0 的某个版本还是有的。
另外,补充,我遇到的问题是:即使 Memory 对象没有调用对应的方案,error log 里面也会提示被锁定,并且程序还会尝试重建用户词典。所以,这个问题我当时并不确定是 Memory 引发的。
但几个月某个改动「修复」(?)了。因为我的配置里面存在用到 Memory 的地方,有的还是不同的 lua 打开同一个方案。同步的时候没有报错。
另外提供一个可能的解决方案(未验证):将在 translator 里面设置 db_class: tabledb。
lua 中的 LevelDb 不支援 db_pool ( 可以多個翻譯器 同時參考己開啓的 userdb) MemoryReg 類似 翻譯器 ,可以同時 和其他翻譯器共用同一個userdb
如果 usredb 先被 LevelDb 開啓 將造成 Translator 無法開啓 userdb 檢查 方案中 lua script 是否有使用 LevelDb 開啓 userdb( LevelDb)
如果一定編輯LevelDb 操作 翻譯器的userdb ,最好是在 rime.lua or init() or fini() ,中操作 且完工時 使用 :close() 關閉
local db = LevelDb('luna_pinyin')
db:open()
....
db:close()
你好 @shewer ,如果在具体的 lua 中,比如:
local F = {}
function F.init( env )
local config = env.engine.schema.config
env.name_space = env.name_space:gsub( '^*', '' )
local schema = config:get_string( env.name_space .. '/en_schema' ) or 'en'
F.en_dict = Memory( env.engine, Schema( schema ) )
env.commit_notifier = env.engine.context.commit_notifier:connect(
function( ctx )
local cand = ctx:get_selected_candidate()
local commit_text = ctx:get_commit_text()
local commit_code = ctx.input
if (cand and cand.text == commit_text) then
if (cand.type == 'sentence' or cand.type == 'raw') and
((commit_text:find( '%a' ) and commit_text:find( '%d' )) or (commit_text:find( '^%a+$' ))) then
log.info( '- record: ' .. commit_text )
F.update_dict_entry( commit_text, commit_code )
end
else
return
end
end
)
end
end
function F.update_dict_entry( text, code )
if #text == 0 then return end
local e = DictEntry()
e.text = text
e.custom_code = code .. ' '
F.en_dict:update_userdict( e, 1, '' )
end
function F.func( input, env )
for cand in input:iter() do yield( cand ) end
end
function F.fina( env )
env.commit_notifier:disconnect()
end
return F
因为脚本中并没有显性地新建 db 这个对象,所以想问这两步是加在哪里呢?
在新建 memory 之前?在 disconnect 之后?
你好 @shewer ,如果在具体的 lua 中,比如:
local F = {} function F.init( env ) local config = env.engine.schema.config env.name_space = env.name_space:gsub( '^*', '' ) local schema = config:get_string( env.name_space .. '/en_schema' ) or 'en' F.en_dict = Memory( env.engine, Schema( schema ) ) env.commit_notifier = env.engine.context.commit_notifier:connect( function( ctx ) local cand = ctx:get_selected_candidate() local commit_text = ctx:get_commit_text() local commit_code = ctx.input if (cand and cand.text == commit_text) then if (cand.type == 'sentence' or cand.type == 'raw') and ((commit_text:find( '%a' ) and commit_text:find( '%d' )) or (commit_text:find( '^%a+$' ))) then log.info( '- record: ' .. commit_text ) F.update_dict_entry( commit_text, commit_code ) end else return end end ) end end function F.update_dict_entry( text, code ) if #text == 0 then return end local e = DictEntry() e.text = text e.custom_code = code .. ' ' F.en_dict:update_userdict( e, 1, '' ) end function F.func( input, env ) for cand in input:iter() do yield( cand ) end end function F.fina( env ) env.commit_notifier:disconnect() end return F
因为脚本中并没有显性地新建 db 这个对象,所以想问这两步是加在哪里呢?
在新建 memory 之前?在 disconnect 之后?
F.fina 錯了 是 F.fini(env) notifier 無法解構容易出問題
Mmory 內己註冊 commit_notifier 用不了嗎? 每次 commit 時就會 callback Memorize() , 參考 memory 範例腳本
感谢指正,不过这个是 typo,复制,脱敏,到这里来的时候不小心打错了。
我本想问本 issue 提到的问题,因为 issue 提到的 error log 我曾经碰到过,但现在没有碰到过了。而本 issue 提到说其他平台仍然会有这个错误。
所以想问你提到的代码,是否能解决这个问题,如果能的话,应该加在脚本的哪里,是新建 Memory 对象前还是后,在 disconnect 前还是后?
local db = LevelDb('luna_pinyin')
db:open()
....
db:close()
如果沒用 LevelDb 就不用在意
Mmory 內己註冊 commit_notifier 用不了嗎? 每次 commit 時就會 callback Memorize() , 參考 memory 範例腳本
这个脚本我记得是为了将 echo_translator 和特定编码的 sentence 候选写入用户词典的,本身候选不属于任何方案内,所以要手动写入。是正常工作且没有 error log 的。
但有一段时间每次同步,发现过本 issue 提到的
IO error: lock .../...userdb/LOCK: already held by process
当时并没有找到问题所在,因为 erro log 中出现了没有在 lua 中调用的方案名,比如 Memory 所用的方案是 en,但 error log 提示 stroke db 无法打开的错误。
过一段时间,突然好了。
现在看到这个 issue,又想起来了。
如果沒用 LevelDb 就不用在意
感谢热情的 shewer 回复,您以往 issue 里的回复对我的帮助很大。
参考我的 1st comment,并没有使用 LevelDb。
如果一定編輯LevelDb 操作 翻譯器的userdb ,最好是在 rime.lua or init() or fini() ,中操作 且完工時 使用 :close() 關閉
如果是这样的话,我抽空用您的 LevelDb 试一下, 可以显性释放 db,感觉应该不会占用 LOCK。整个操作可以放在 func
里么?
我可以确定,这个问题在 15.0 的某个版本还是有的。
那我不太清楚了,我的 weasel
0.15.0 没有这个问题,weasel
代码我没怎么看过,具体为啥会(帮助/不)解锁 LOCK 不太清楚。
另外,补充,我遇到的问题是:即使 Memory 对象没有调用对应的方案,error log 里面也会提示被锁定,并且程序还会尝试重建用户词典。所以,这个问题我当时并不确定是 Memory 引发的。
我这里试过了,把 Memory 关掉后,其他全部不动,不会出现上述报错,可以正常 sync 了 (否则 LOCK 相关用户词典无法 sync)。您的情况我不是太清楚,可能和我的有点区别。
当时并没有找到问题所在,因为 erro log 中出现了没有在 lua 中调用的方案名,比如 Memory 所用的方案是 en,但 error log 提示 stroke db 无法打开的错误。
您提到 stroke,看到您写的 search.lua
代码(已 merge 到 rime-ice),可能和 ReverseLookup
有关系,本质上应该都和 db 有关,属于同一类问题 (目前我只在反查里看到有用过 stroke
)。
我 Reverse 词典基本上不用,不是太懂。
在这里感谢您给 rime-ice 写的那么多 lua 代码,方便了 lua 新手写 lua 代码。
另外提供一个可能的解决方案(未验证):将在 translator 里面设置 db_class: tabledb。
嗯,感谢您的建议。目前我也是这样处理的(见 imfuxiao/Hamster#593)
LevelDb 主要是提供 librime-lua 大量的 key value db 應用 雖然可以開啓 userdict.userdb 但是要小心 操作 我是不會在librime 工作程序中 開啓 userdict
LevelDb 主要是提供 librime-lua 大量的 key value db 應用 雖然可以開啓 userdb 但是要小心 操作
看文档里写到 '不可用於 已開啓 的userdb, 專用於 librime-lua key-value db' 这样的话,我把我的需求再详细描述一下吧,您帮忙看下有没有什么更好的办法?
当输入 'foobarfoo' 其用特定字符如'' 结尾时,将其标记为 自造英文词汇 并写入词典,最好可以像用户词典那样,不需要重新部署就可以记忆(即 下一次输入时,会列入候选项)。
替換 echo_translator , 檢查 inp 字尾 * 時將inp 製作成 Phras
使用 env.name_space 指定方案
--echo_translator.lua
local function memoryCallback1(commits)
for i, dictentry in ipairs(commits:get()) do
commits:update_entry(dictentry, 1, "")
end
end
local T = {}
function T.init(env)
env.name_space = env.name_space:match("[^*]*$")
env.mem = Memory(env.engine,Schema(env.name_space) ) -- ns= "translator"
env.mem:memorize(memoryCallback1)
end
function T.fini(env)
env.mem = nil
end
function T.func(inp,seg,env)
if inp:match("%*$") then
local entry = DictEntry()
entry.text = inp:sub(1, -2)
entry.custom_code = entry.text .. " " -- 必須加上 空白
local ph = Phrase(env.mem, "userdict", seg._start, seg._end, entry)
ph.quility = -100
yield(ph:toCandidate())
else
local cand = Candidate('raw', seg._start, seg._end, inp, "RAW")
cand.quality = -100
yield(cand)
end
end
return T
使用 Schema(env.name_space)
配合 lua_translator@en_custom_translator
确实不会 LOCK,因为使用的词典是只属于此 lua 脚本的。
但是,此时 env.name_space=en_custom_translator
,应该需要一个对应的 en_custom_translator.schema.yaml
。然后,主 schema.yaml 添加 table_translator@en_custom_translator
来导入自造词。这样的话依然会有 "Error opening db 'en_custom_translator': IO error: lock .../en_custom_translator.userdb/LOCK: already held by process" message。"
暂时可以切换方案来同步 (即只使用一个 schema 来自造英文词汇),因为 lua_translator@en_custom_translator
是只属于一个 schema 的。之前由于几个 schema 都用了 lua_translator@en_custom_translator
(名字不一样,不过都调用同一个 lua 脚本。),故切换方案无效。
请问有办法做 db:close()
类似的操作于 userdb,从而不切换方案也可同步?(在 hamster 上,切换方案并不会自动释放 LOCK。见 问题里引用的第2个链接 fcitx/fcitx5-rime#97)
上述方案可以通过 yield(ph:toCandidate())
产生 candidate。但是 env.mem:memorize(memoryCallback1)
并不运行 memoryCallback1
(在此函数最开始加了 log.info
,但是 log 里并不产生此 info)。
不清楚上述 commits:update_entry
是不是一个 pseudo code,文档里用的是 memory:update_userdict
(memory是一个 Memory
instance)。
Weasel 在同步的时候,会进入【维护模式】,释放占用的文件资源。或许因此不会出现 LOCK 的情况。
不清楚这种维护模式是否是所有前端都有的逻辑,也不清楚在这种维护模式下,lua 是否会按设计释放资源。
但是,此时
env.name_space=en_custom_translator
,应该需要一个对应的en_custom_translator.schema.yaml
。
这个结论怎么来的
刚刚试了下 leveldb,改了下 repo 的 sample,可以作为用户词典用,比如你的需求:
结尾是 * 号,写入用户词典,无须重新部署就能使用
演示:shei* -> shei
-- leveldb.lua
-- - lua_translator@*leveldb@dict
-- dict:
-- dictionary: lua
-- initial_quality: 1.5
db_pool_ = {}
local function opendb( name )
local db = db_pool_[name]
if not db then
db = LevelDb( name )
if not db then return nil end
db_pool_[name] = db
end
if not db:loaded() then db:open() end
return db
end
local function update_entry( text, code, db )
if #text == 0 then return end
db:update( code, text )
end
local M = {}
function M.init( env )
local config = env.engine.schema.config
local dbname = config:get_string( env.name_space .. '/dictionary' )
env.quality = tonumber( config:get_string( env.name_space .. '/initial_quality' ) ) or 1
env.db = assert( opendb( dbname ), 'initleveldb failed' )
env.commit_notifier = env.engine.context.commit_notifier:connect(
function( ctx )
local commit_text = ctx:get_commit_text()
local commit_code = ctx.input
local cand = ctx:get_selected_candidate()
local cand_text = cand.text
if (cand and cand_text == commit_text and cand.type == 'new') then
commit_code = commit_code:gsub( '*$', '' )
print( '- record: ' .. commit_text .. ' | ' .. commit_code )
update_entry( commit_text, commit_code, env.db )
end
end
)
end
function M.fini( env )
env.db:close()
env.commit_notifier:disconnect()
end
function M.func( inp, seg, env )
if inp:find('*$') then
local cand = Candidate('new', seg.start, seg._end, inp:gsub('*$',''), '+')
yield(cand)
end
for _, v in env.db:query( inp ):iter() do
local type = 'lua'
local cand = Candidate( type, seg.start, seg._end, v, '#' )
cand.quality = env.quality
yield( cand )
end
end
return M
不过我用 rime_dict_manager 没办法导出数据,不晓得怎么方便地备份里面的 key value
使用
Schema(env.name_space)
配合lua_translator@en_custom_translator
确实不会 LOCK,因为使用的词典是_只属于此 lua 脚本_的。如果方案中有 該字典的 namespace 可以參考 下面 create memory 的參數
Memory( engine, schema[,ns]) Memory(env.engine, env.engine.schema) -- 使用用本方案ns="translator" Momory(env.engine, Schema('cangjie5') ) -- 使用外部方案 ns=translator Memory(env.engine, Schema('cangjie5'), 'cangjie6') ns='cangjie6'
但是,此时
env.name_space=en_custom_translator
,应该需要一个对应的en_custom_translator.schema.yaml
。然后,主 schema.yaml 添加table_translator@en_custom_translator
来导入自造词。这样的话依然会有 "Error opening db 'en_custom_translator': IO error: lock .../en_custom_translator.userdb/LOCK: already held by process" message。"暂时可以切换方案来同步 (即只使用一个 schema 来自造英文词汇),因为
lua_translator@en_custom_translator
是只属于一个 schema 的。之前由于几个 schema 都用了lua_translator@en_custom_translator
(名字不一样,不过都调用同一个 lua 脚本。),故切换方案无效。请问有办法做
db:close()
类似的操作于 userdb,从而不切换方案也可同步?(在 hamster 上,切换方案并不会自动释放 LOCK。见 问题里引用的第2个链接 fcitx/fcitx5-rime#97)
上述方案可以通过
yield(ph:toCandidate())
产生 candidate。但是env.mem:memorize(memoryCallback1)
并不运行memoryCallback1
(在此函数最开始加了log.info
,但是 log 里并不产生此 info)。不清楚上述
commits:update_entry
是不是一个 pseudo code,文档里用的是memory:update_userdict
(memory是一个Memory
instance)。
新版本 librime-lua 已在 CommitEntryReg 加入 update_entry methods , 所以不須要引用 upvalue 舊版本是使用 upvalue 機制取得 mem
--old
env.mem:memorize( function(commits) callback(env.mem,commits) end) -- callback(mem,commits)
--new
env.mem:memorize( callback1) -- callback1(commits)
剛剛 測試 ,在mem 中開啓的 userdb , 無法再用 leveldb 操作
mem 生成後 userdb 始終開啓,LevelDb 也是無法開啓,
mem.user_dict.loaded -- true
leveldb._loaded -- false
leveldb:close() -- false , close() 程序會先檢查 loaded 狀態再執行 ,所以 close 失敗
leveldb:open() -- error 無法開啓
我在 fini 中 加入 env.mem = nil 提前清除 期望提前 gc , 你可試試, rime_api_console 版本 ver : " (id:unknown) Ver: librime 1.11.0 librime-lua 287 lua Lua 5.4"
请教一个问题,这个 leveldb 的数值可以像 librime 的 userdb 借助 librime cli 导出、合并吗?@shewer
似乎 rime_dict manager 导出的是只有文件头的空内容,
倒是发现用
for k,v in env.db:query(''):iter() do
print (k,v)
end
能获取一份内容。是不是只能在 lua 里面这些合并、导出的操作。
比如说这个根据 sample 做小小改动的脚本 https://github.com/hchunhui/librime-lua/issues/330#issuecomment-2063339939
但是,此时
env.name_space=en_custom_translator
,应该需要一个对应的en_custom_translator.schema.yaml
。这个结论怎么来的
@mokapsing 实际效果是这样 您可以自己试一下。不过这个不是重点,因为这个参数可以自己配置。
新版本 librime-lua 已在 CommitEntryReg 加入 update_entry methods , 所以不須要引用 upvalue 舊版本是使用 upvalue 機制取得 mem
我用的是旧版本,要对您的代码做点小修改用在自己电脑上。
我在 fini 中 加入 env.mem = nil 提前清除 期望提前 gc ,
@shewer 加了 log 进行查看,确实会提前 env.mem = nil
,然后,进行同步。但是 LOCK 并没有释放。
我 Fcitx5 用的是 Arch Linux 编译好的 1.10.0
请教一个问题,这个 leveldb 的数值可以像 librime 的 userdb 借助 librime cli 导出、合并吗?@shewer
似乎 rime_dict manager 导出的是只有文件头的空内容,
倒是发现用
for k,v in env.db:query(''):iter() do print (k,v) end
能获取一份内容。是不是只能在 lua 里面这些合并、导出的操作。
比如说这个根据 sample 做小小改动的脚本 #330 (comment)
沒深入了解,可以看 librime/tools/ 中code ,說不定 librime-lua userdb api 可以完成 ,前提是在 engine/translators 導入前 完成 userdb 處理井 close() ,避免 translator 無法開啓 userdict
同步時不會把user_dict 導出嗎?
我在 fini 中 加入 env.mem = nil 提前清除 期望提前 gc ,
@shewer 加了 log 进行查看,确实会提前
env.mem = nil
,然后,进行同步。但是 LOCK 并没有释放。我 Fcitx5 用的是 Arch Linux 编译好的 1.10.0
librime-lua git版本 ix: reduce peak memory usage (7f3eca2ce6 )
查查 librime-lua 的版本
local function find_ver()
if KeyEvent(0x41,0):repr() == "A" then
return 321
elseif Component and Component.TableTranslator then
return 287
elseif UserDb and TableDb then
return 240
elseif UserDb then
return 220
elseif rime_api.regex_match then
return 197
elseif rime_api.get_distribution_name then
return 185
elseif LevelDb then
return 177
elseif Opencc then
return 147
elseif KeySequence and KeySequence().repr then
return 139
elseif ConfigMap and ConfigMap().keys then
return 127
elseif Projection then
return 102
elseif KeyEvent then
return 100
elseif Memory then
return 80
elseif rime_api.get_user_data_dir then
return 9
elseif log then
return 9
else
return 0
end
end
玩了一会 leveldb,用做临时的词典的话,刚刚能用。
导入导出不成问题,但权重可能有点麻烦,让 GitHub Copilot 根据样例写了个大概:
但还是想搞清楚这个 issue 提到的问题如何解决
https://github.com/mirtlecn/rime/blob/master/tools/lua/leveldb.lua
感谢您提供的方案。试了一下,这样即使多个 schema 共用这个 db 也没有问题。因为 db.close()
释放资源,同时 sync
完全分离出来了。(不过 sync
暂时需要全部手写,上述支持导入导出,不同设备 sync 需要检测重复的 entry 然后 merge。)
这样规避了 table_translator
,从而不会有两个 translator 抢占一个 LOCK 的情况。
感觉和原本 rime-fast-xhup
的 en_custom.lua
很类似,只不过 rime-fast-xhup
调用 file
添加词条,sync
也是通过 file
。由于检索是完全匹配,并不是 prefix,故需要重新部署导入词典的新词。做一点小修改 (问问题之前应该想到这种方法的。感谢大家的热心帮助):
local file = assert(io.open(dict_path, "r")) --打开
for line in file:lines() do
local cand_text=string.match(line,'^(.*)\t'..input)
if cand_text then
yield(Candidate("en_custom", seg.start, seg._end, cand_text,'(manual search)'))
end
end
file:close()
然后和上面一样,不用 table_translator
。
这样的话相当于两种方案,LevelDb
或 file
,基本原理是类似的。
@shewer 可以再问一个和本 issue 相关的小问题么?
我想着用 rime 默认删词按键 ^K 来删除上述手动管理的 tabledb 某一个词汇,可以通过 lua_processor 删除 (返回2方便后续lua_translator更新),但是无法像 rime 删除词汇那样及时更新 menu
。我是用上一个 comment 方法来手动产生 candidate 的。
现象是通过 log 查看,后续的 lua_translator 和 lua_filter 都不调用 (在仅按下 ^K 情况下)。
librime 里应该是通过 RefreshNonConfirmedComposition
里的 update_notifier_
刷新的,但是 lua_translator 和 lua_filter 好像并不接受 notifier, 除非输入 alphabet 里的字符。
SegmentReg select_index ContextReg delete_current_selection() 會蜀發 delete notifier 查看 memory.cc OnDeleteEntry() 處理完,會用RefreshNonConfirmedComposition() 刷新 ,重新Compose()
│ void Memory::OnDeleteEntry(Context* ctx) {
│ if (!user_dict_ || user_dict_->readonly() || !ctx || !ctx->HasMenu())
│ return;
│ auto phrase =
│ As<Phrase>(Candidate::GetGenuineCandidate(ctx->GetSelectedCandidate()));
│ if (Language::intelligible(phrase, this)) {
│ const DictEntry& entry(phrase->entry());
│ LOG(INFO) << "deleting entry: '" << entry.text << "'.";
│ user_dict_->UpdateEntry(entry, -1); // mark as deleted in user dict
│ ctx->RefreshNonConfirmedComposition();
│ }
│ }
感谢。之前弄错了,由于 lua_translator 直接用 Candidate
construct 候选项,As<Phrase>
会变成 NULL
,所以之前 lua 里用 delete_current_selection
无效。
lua 里直接调用 refresh_non_confirmed_composition
来刷新 kGuess
相关 candidate,可行。
發現 user_dict 是leveldb 時 須要操作 StartSession() FinishSession() DiscardSession() Memory::OnCommit() 時須要 call StartSession() TableTranslation ScriptTranslation 在產生 Translation 前 會 call FiniSession()
在 callback 外部操作 update_entry 會少了 StartSession() 操作
Memory::OnCommit() https://github.com/rime/librime/blob/1f3bf35691a5ad066ef36775e289525f3b092e28/src/rime/gear/memory.cc#L104
TableTranslaion ScriptTranslation 開始時 call FinishSession() https://github.com/rime/librime/blob/1f3bf35691a5ad066ef36775e289525f3b092e28/src/rime/gear/table_translator.cc#L249
class LevelDb https://github.com/rime/librime/blob/1f3bf35691a5ad066ef36775e289525f3b092e28/src/rime/dict/level_db.h#L35
这个 bug 打算如何处理呢?
是禁止在 memory callback 外使用 update_entry,抛出明确的错误或者警告,还是如何?
Based on
rime-fast-xhup
and this issue, I use the following to write one custom word to the userdb.Although it has the advantage of loading the committed word automatically without re-deploy, but it will throw
Error opening db '...': IO error: lock .../...userdb/LOCK: already held by process
error when sync in Fcitx5, etc., but not in weasel where the latter probably solves with this situation inherently. Then we can usefile
module to manually append the entry to the YAML or txt file, but that will need re-deploy.Is there one way to avoid the above error but also keep the above advantage?