erlang - Mnesia 查询游标 - 在实际应用中使用它们

标签 erlang cursor erlang-otp mnesia yaws

在大多数应用程序中,很难避免需要查询用户想要浏览的大量信息。这就是我使用光标的原因。对于 mnesia,光标是使用 qlc:cursor/1 or qlc:cursor/2 实现的。 。与他们合作了一段时间并多次遇到这个问题后,

11> qlc:next_answers(QC,3).
** exception error: {qlc_cursor_pid_no_longer_exists,<0.59.0>}
     in function  qlc:next_loop/3 (qlc.erl, line 1359)
12>
我突然想到,整个游标事件必须在一个 mnesia 事务中:作为一个整体执行一次。像下面这样
E:\>erl
Eshell V5.9  (abort with ^G)
1> mnesia:start().
ok
2> rd(obj,{key,value}).
obj
3> mnesia:create_table(obj,[{attributes,record_info(fields,obj)}]).
{atomic,ok}
4> Write = fun(Obj) -> mnesia:transaction(fun() -> mnesia:write(Obj) end) end.
#Fun<erl_eval.6.111823515>
5> [Write(#obj{key = N,value = N * 2}) || N <- lists:seq(1,100)],ok.
ok
6> mnesia:transaction(fun() -> 
            QC = cursor_server:cursor(qlc:q([XX  || XX <- mnesia:table(obj)])),
            Ans = qlc:next_answers(QC,3),
            io:format("\n\tAns: ~p~n",[Ans]) 
    end).
        Ans: [{obj,20,40},{obj,21,42},{obj,86,172}]
{atomic,ok}
7>
当您尝试在 mnesia 事务之外调用 say: qlc:next_answers/2 时,您将收到异常。不仅在事务之外,而且即使该方法是由与创建游标的进程不同的进程执行的,也必然会发生问题。

另一个有趣的发现是,一旦退出 mnesia 事务,mnesia 游标涉及的进程之一(显然 mnesia 在后台生成一个进程)就会退出,导致游标无效。看看下面这个:
-module(cursor_server).
-compile(export_all).
cursor(Q)-> case mnesia:is_transaction() of false -> F = fun(QH)-> qlc:cursor(QH,[]) end, mnesia:activity(transaction,F,[Q],mnesia_frag); true -> qlc:cursor(Q,[]) end.
%% --- End of module -------------------------------------------
然后在 shell 中,我使用该方法:
7> QC = cursor_server:cursor(qlc:q([XX  || XX <- mnesia:table(obj)])).
{qlc_cursor,{<0.59.0>,<0.30.0>}}
8> erlang:is_process_alive(list_to_pid("<0.59.0>")).
false
9> erlang:is_process_alive(list_to_pid("<0.30.0>")).
true
10> self().
<0.30.0>
11> qlc:next_answers(QC,3).
** exception error: {qlc_cursor_pid_no_longer_exists,<0.59.0>}
     in function  qlc:next_loop/3 (qlc.erl, line 1359)
12>
因此,这使得构建一个用户需要浏览一组特定结果的 Web 应用程序变得非常困难,按组说:给他/她前 20 个,然后是下 20 个,等等。这涉及到获取第一个结果,将它们发送到网页,然后等待用户单击 NEXT,然后询问 qlc:cursor/2 接下来的 20 个结果,依此类推在。当卡在 mnesia 事务中时,这些操作无法完成!唯一可能的方法是生成一个卡在那里的进程,以消息形式接收和发送回下一个答案,并以消息形式接收 next_answers 请求,如下所示:

-define(CURSOR_TIMEOUT,timer:hours(1)).

%% initial request is made here below
request(PageSize)->
    Me = self(),    
    CursorPid = spawn(?MODULE,cursor_pid,[Me,PageSize]),
    receive
        {initial_answers,Ans} -> 
            %% find a way of hidding the Cursor Pid
            %% in the page so that the subsequent requests
            %% come along with it
            {Ans,pid_to_list(CursorPid)}
    after ?CURSOR_TIMEOUT -> timedout
    end.

cursor_pid(ParentPid,PageSize)->
    F = fun(Pid,N)-> 
            QC = cursor_server:cursor(qlc:q([XX  || XX <- mnesia:table(obj)])),
            Ans = qlc:next_answers(QC,N),
            Pid ! {initial_answers,Ans},
            receive
                {From,{next_answers,Num}} ->
                    From ! {next_answers,qlc:next_answers(QC,Num)},
                    %% Problem here ! how to loop back
                    %% check: Erlang Y-Combinator
                delete -> 
                    %% it could have died already, so we be careful here !
                    try qlc:delete_cursor(QC) of 
                        _ -> ok 
                    catch 
                        _:_ -> ok 
                    end,
                    exit(normal)
            after ?CURSOR_TIMEOUT -> exit(normal)
            end
        end,
    mnesia:activity(transaction,F,[ParentPid,PageSize],mnesia_frag).

next_answers(CursorPid,PageSize)->
    list_to_pid(CursorPid) ! {self(),{next_answers,PageSize}},
    receive
        {next_answers,Ans} ->
            {Ans,pid_to_list(CursorPid)}
    after ?CURSOR_TIMEOUT -> timedout
    end.

这将产生管理进程退出、跟踪/监控等更复杂的问题。我想知道为什么 mnesia 实现者没有看到这一点!

现在,这引出了我的问题。我一直在网上寻找解决方案,您可以查看出现问题的这些链接:mnemosyne , Ulf Wiger's Solution to Cursor Problems , AMNESIA - an RDBMS implementation of mnesia .

1. 有谁知道如何以与记录的方式不同的方式处理 mnesia 查询游标,并且值得分享吗?

2. mnesia implemeters 决定在单个事务中强制使用游标的原因是什么:甚至是对 next_answers 的调用?

3. 从我所呈现的内容来看,有什么我不太明白的地方吗(除了我糟糕的错误插图代码 - 请忽略那些)?

4. AMNESIA(在我上面给出的链接的第 4.7 节上)具有良好的游标实现,因为对 next_answers 的后续调用不需要位于同一事务中,也不需要由同一进程进行。您是否会建议任何人因此从遗忘症切换到失忆症,而且这个库是否仍然受支持?

5. Ulf Wiger(许多 erlang 库,特别是 GPROC 的作者)建议使用 mnesia:select/4 。我如何使用它来解决 Web 应用程序中的光标问题?

注意:请不要建议我离开 mnesia 并使用其他东西,因为我想使用 mnesia 来解决这个特定问题。感谢您花时间阅读所有这些问题。

最佳答案

动机是事务获取(在您的情况下)读锁。 锁不能保留在事务之外。

如果你愿意,你可以在 dirty_context 中运行它,但是你会失去 事务属性,即表可能在调用之间发生变化。

make_cursor() ->
    QD = qlc:sort(mnesia:table(person, [{traverse, select}])),
    mnesia:activity(async_dirty, fun() -> qlc:cursor(QD) end, mnesia_frag).

get_next(Cursor) ->
    Get = fun() -> qlc:next_answers(Cursor,5) end,
    mnesia:activity(async_dirty, Get, mnesia_frag).

del_cursor(Cursor) ->
    qlc:delete_cursor(Cursor).

关于erlang - Mnesia 查询游标 - 在实际应用中使用它们,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11967660/

相关文章:

erlang - Erlang集群

javascript - 停止光标,直到 javascript 中的文本区域中的文本发生更改

networking - Erlang:以编程方式启动远程节点

erlang - Elixir-Erlang : is there a "reasonable" limit of children handled by a supervisor?

erlang - 如何使用 cookie 和 erl 选项启动 iex session

erlang - mochiweb 和 gen_server

openssl - Tsung 在测试 https 时遇到 crypto.so 问题

erlang - 名为timer_server的gen_server导致定时器模块函数不返回

使用 WM_SETCURSOR C++ 光标更改为沙漏

java - 更改光标以移交 JFrame 中的某些坐标