使用 Erlang 客户端通过协议缓冲区从 Riak 集中删除元素而不获取整个集合

Deleting an element from a Riak set via protocol buffers using the Erlang client without fetching the whole set

通过 HTTP API,我们可以从集合中删除任意元素而无需获取全部内容:

curl -X POST http://127.0.0.1:8098/types/sets/buckets/travel/datatypes/cities -H "content-type: application/json" -d '{ "remove" : "Toronto" }'

(验证: tcpdump -i any -s 0 -n 'src port 8087 or src port 8098 and host 127.0.0.1')

然而,通过 protocol buffers 客户端,我们需要执行以下步骤才能从集合中删除元素:

{ok, MySet} = case riakc_pb_socket:fetch_type(Pid, {<<"sets">>, <<"travel">>}, <<"cities">>) of {error,{notfound,set}}-> {ok, riakc_set:new()}; {ok, Set} -> {ok, Set}  end.
ModSet=riakc_set:del_element(lists:last(ordsets:to_list(riakc_set:value(MySet))), MySet).
riakc_pb_socket:update_type(Pid, {<<"sets">>, <<"travel">>}, <<"cities">>, riakc_set:to_op(ModSet)).

顾名思义,riakc_pb_socket:fetch_type 检索整个集合。我在 Erlang 客户端中找不到任何使用 protobuf 发送删除请求而不先检索整个集合的方法。

有没有办法避免在删除元素时通过 protobuf 客户端获取整个集合对象?

更新:协议缓冲区 API 更新数据类型似乎很有用:

riakc-pb-socket:modify_type (source code) 的最后一个参数是一组更改。如果您已经知道要删除哪个元素,理论上您可以创建一个新的空集并构建一个删除操作

Empty = riakc_set:new(Context),
Removal = riakc_set:del_element(<<"Toronto">>,Empty),
Op = riakc_set:to_op(Removal),
riakc_pb_socket:update_type(Pid, {<<"sets">>, <<"travel">>}, <<"cities">>, Op).

这里的关键是Context,它是由服务器生成的不透明值。您可以在没有请求的情况下发送请求,或者发送一个空请求 (<<>>),但这可能不是 Good Thing(tm)。上下文是 Riak 确定因果关系的方式。每次采取行动时,每个参与者都会更新它,并用于确定最终的一致值。因此,如果您在没有上下文的情况下发送一个集合操作,它可能会失败或被乱序处理,尤其是在大约同时发生任何其他更新的情况下。

在 HTTP API 的情况下,协调器获取整个对象以获取上下文,然后使用该上下文提交操作。

执行常规 get 操作时,您可以在选项中指定 head 以仅取回元数据,其中包括上下文,但不包括数据。我还没有用 fetch_type 测试过,但收敛类型可能有类似的功能。如果有,您只需要获取头部以获取上下文,然后使用该上下文提交您的操作。

-编辑-
根据 docs:

%% You cannot fetch a Data Type's context directly using the Erlang
%% client. This is actually quite all right, as the client automatically
%% manages contexts when making updates.

您似乎可以将一个 fun 传递给 riakc_pb_socket:modify_type,这样您就不必显式获取旧值,但这只会在幕后获取它,所以您实际上只保存了一个一小段代码。

riakc_pb_socket:modify_type(Pid,
            fun(MySet) -> riakc_set:del_element(lists:last(ordsets:to_list(riakc_set:value(MySet))), MySet)
            end, {<<"sets">>, <<"travel">>}, <<"cities">>,[create]).