Giter Club home page Giter Club logo

riak-elixir-client's Introduction

Riak Elixir Client

Build Status Hex version Hex downloads Stories in Ready

A Riak client written in Elixir. Now includes connection pooling with pooler and a variety of other improvements from riex.

Setup

Prerequisites

  • Riak 2.0+
  • Elixir 1.0+

In an Elixir application

Add the following to mix.exs

...
def application do
  [ applications: [ :riak ]]
end
...
defp deps do
  [ {:riak, "~> 1.1.6"} ]
end
...

Usage

Establishing a Riak connection

{:ok, pid} = Riak.Connection.start_link('127.0.0.1', 8087) # Default values

Connection Pooling

Most functions in this module can be called by passing the pid of the established connection or using a pool of connections (provided by pooler). Define pools by using the group riak. Following is an example config/config.exs:

config :pooler, pools:
  [
    [
      name: :riaklocal1,
      group: :riak,
      max_count: 10,
      init_count: 5,
      start_mfa: { Riak.Connection, :start_link, [] }
    ], [
      name: :riaklocal2,
      group: :riak,
      max_count: 15,
      init_count: 2,
      start_mfa: { Riak.Connection, :start_link, ['127.0.0.1', 9090] }
    ]
  ]

For an example using this functionality with a local Riak instance, check config/config.exs. More information about Elixir configuration can be found on http://elixir-lang.org: Application environment and configuration.

Once a pool configuration is properly defined in a project, calls to Riak can omit the pid. For example:

This call uses a pid from the pool of connections provided by pooler:

Riak.delete("user", key)

This call requires a pid obtained by first calling Riak.Connection.start_link:

Riak.delete(pid, "user", key)

Save a value

o = Riak.Object.create(bucket: "user", key: "my_key", data: "Han Solo")
Riak.put(pid, o)

Find an object

o = Riak.find(pid, "user", "my_key")

Update an object

o = %{o | data: "Something Else"}
Riak.put(pid, o)

Delete an object

Using key

Riak.delete(pid, "user", key)

Using object

Riak.delete(pid, o)

Timeseries

Riak Timeseries functionality is available in TS 1.3.1 releases of Riak and greater.

Setup

Create a table:

riak-admin bucket-type create GeoCheckin '{"props":{"table_def": "CREATE TABLE GeoCheckin (region VARCHAR NOT NULL, state VARCHAR NOT NULL, time TIMESTAMP NOT NULL, weather VARCHAR NOT NULL, temperature DOUBLE, PRIMARY KEY ((region, state, QUANTUM(time, 15, 'm')), region, state, time))"}}'
riak-admin bucket-type activate GeoCheckin

Insert Rows

Riak.Timeseries.put("GeoCheckin", [
    {"region1", "state1", 25, "hot", 23.0},
    {"region2", "state99", 26, "windy", 19.0}
])
> :ok

Get a row by primary key

Riak.Timeseries.get("GeoCheckin", ["region1", "state1", 25])
> {["region", "state", "time", "weather", "temperature"], [{"region1", "state1", 25, "hot", 23.0}]}

Get all rows

Note: This is a very expensive operation for a loaded cluster

Riak.Timeseries.list!("GeoCheckin")
> [{"region1", "state1", 25, "hot", 23.0}, {"region2", "state99", 26, "windy", 19.0}]

Delete a row

Riak.Timeseries.delete("GeoCheckin", ["region2", "state99", 26])
> :ok

Query

Riak.Timeseries.query("select * from GeoCheckin where time > 24 and time < 26 and region = 'region1' and state = 'state1'")
> {["region", "state", "time", "weather", "temperature"], [{"region1", "state1", 25, "hot", 23.0}]}

Datatypes

Riak Datatypes (a.k.a. CRDTs) are avaiable in Riak versions 2.0 and greater. The types included are: maps, sets, counters, registers and flags.

Setup

Datatypes require the use of bucket-types. Maps, sets, counters, and hyper-log-logs can be used as top-level bucket-type datatypes; Registers and flags may only be used within maps.

The following examples assume the presence of 4 datatype enabled bucket-types. You can create these bucket-types by running the following commands on a single Riak node in your cluster:

Bucket-Type: counters

riak-admin bucket-type create counters '{"props":{"datatype":"counter"}}'
riak-admin bucket-type activate counters

Bucket-Type: sets

riak-admin bucket-type create sets '{"props":{"datatype":"set"}}'
riak-admin bucket-type activate sets

Bucket-Type: maps

riak-admin bucket-type create maps '{"props":{"datatype":"map"}}'
riak-admin bucket-type activate maps

Bucket-Type: hll

riak-admin bucket-type create hll '{"props":{"datatype":"hll"}}'
riak-admin bucket-type activate hll

Counters

Create a counter (alias Riak.CRDT.Counter):

Counter.new
  |> Counter.increment
  |> Counter.increment(2)
  |> Riak.update("counters", "my_counter_bucket", "my_key")

Fetch a counter:

counter = Riak.find("counters", "my_counter_bucket", "my_key")
  |> Counter.value

counter will be 3.

NOTE: "Counter drift" is a possibility that needs to be accounted for with any distributed system such as Riak. The problem can manifest itself during failure states in either your applicaiton or Riak itself. If an increment operation fails from the client's point of view, there is not sufficient information available to know whether or not that call made it to zero or all of the replicas for that counter object. As such, if the client attempts to retry the increment after recieving something like a error code 500 from Riak, that counter object is at risk of drifting positive. Similarly if the client decides not to retry, that counter object is at risk of drifting negative.

For these reasons, counters are only suggested for use-cases that can handle some (albeit small) amount of counter drift. Good examples of appropriate use-cases are: Facebook likes, Twitter retweet counts, Youtube view counts, etc. Some examples of poor use-cases for Riak counters are: bank account balances, anything related to money. It is possible to implement these types of solutions using Riak, but more client side logic is necessary. For an example of a client-side ledger with tunable retry options, check github.com/drewkerrigan/riak-ruby-ledger. Another approach could be the client-side implementation of a HAT (Highly Available Transaction) algorithm.

Sets

Create a set (alias Riak.CRDT.Set):

Set.new
  |> Set.put("foo")
  |> Set.put("bar")
  |> Riak.update("sets", "my_set_bucket", "my_key")

And fetch the set:

set = Riak.find("sets", "my_set_bucket", "my_key")
  |> Set.value

Where set is an orddict.

Maps

Maps handle binary keys with any other datatype (map, set, flag, register and counter).

Create a map (alias Riak.CRDT.Map):

register = Register.new("some string")
flag = Flag.new |> Flag.enable
Map.new
  |> Map.put("k1", register)
  |> Map.put("k2", flag)
  |> Riak.update("maps", "my_map_bucket", "map_key")

And fetch the map:

map = Riak.find("maps", "my_map_bucket", key) |> Map.value

Where map is an orddict.

Hyper Log Logs

The use case for this type is counting distinct elements in a monotonic way. I think of it as like a counter for customers visited but once a customer visits the counter will never go up again. It also isn't possible to remove a element once it has been added to the log.

Create a HLL (alias Riak.CRDT.HyperLogLog):

HyperLogLog.new
  |> HyperLogLog.add_element("foo")
  |> Riak.update("hll", "my_hll_bucket", "hll_key")

And fetch the distinct count:

hll = Riak.find("hll", "my_hll_bucket", "hll_key") |> HLL.value

Where hll is an integer.

Examples

Check the examples/ directory for a few example elixir applications using the riak client.

For more functionality, check test/ directory.

Tests

MIX_ENV=test mix do deps.get, test

NOTE: If you see errors related to {:error, :nil_object}, Ensure that you have created and activated the below map, set, and counter bucket types.

Note

The creation of the following CRDT bucket-types is a prerequisite for passing the CRDT tests.

riak-admin bucket-type create maps '{"props":{"datatype":"map"}}'
riak-admin bucket-type activate maps
riak-admin bucket-type create sets '{"props":{"datatype":"set"}}'
riak-admin bucket-type activate sets
riak-admin bucket-type create counters '{"props":{"datatype":"counter"}}'
riak-admin bucket-type activate counters
riak-admin bucket-type create hll '{"props":{"datatype":"hll"}}'
riak-admin bucket-type activate hll

Note

The creation of this Timeseries table is a prerequisite for passing the Timeseries tests.

riak-admin bucket-type create GeoCheckin '{"props":{"table_def": "CREATE TABLE GeoCheckin (region VARCHAR NOT NULL, state VARCHAR NOT NULL, time TIMESTAMP NOT NULL, weather VARCHAR NOT NULL, temperature DOUBLE, PRIMARY KEY ((region, state, QUANTUM(time, 15, 'm')), region, state, time))"}}'
riak-admin bucket-type activate GeoCheckin

License

Copyright 2017 Drew Kerrigan.
Copyright 2014 Eduardo Gurgel.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

riak-elixir-client's People

Contributors

chvanikoff avatar dams avatar danieldent avatar drewkerrigan avatar eliaw avatar gausby avatar lowks avatar lucasallan avatar randysecrist avatar stephenmoloney avatar waffle-iron avatar weixiyen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

riak-elixir-client's Issues

Question

Hi Drew,

This isn't an issue, just a question. Does your library support nesting Riak.CRDT.Maps inside of each other and parsing the data back out? Riak.CRDT.Maps.get(map, :map, key) works at the first level, but I can't figure out how to dig deeper than that. I didn't see any code that looked like it would handle that situation in your repository, but I thought I'd ask.

Thanks.

Support riakc_pb_socket:start(_link)/3

Currently it's impossible to run riakc_pb_socket:start(_link)/3, only /2 is supported.
It seems to be pretty straightforward adding this functionality and I can take this if you don't mind.

Unable to set a disabled flag on a map

I don't know if this is the desired behaviour, but I seems that disabling a flag on a map will cause an error to be raised. The following will show an iex session where a disabled flag is set on a map, which will cause a crash, and one where an enabled flag will be set correctly on a map.

iex(1)> Riak.CRDT.Map.new |> Riak.CRDT.Map.put("test", Riak.CRDT.Flag.new |> Riak.CRDT.Flag.disable)
** (throw) :context_required
    (riakc) src/riakc_flag.erl:102: :riakc_flag.disable/1
iex(1)> Riak.CRDT.Map.new |> Riak.CRDT.Map.put("test", Riak.CRDT.Flag.new |> Riak.CRDT.Flag.enable)
{:map, [], [{{"test", :flag}, {:flag, false, :enable, :undefined}}], [],
 :undefined}
iex(2)> 

We have discussed this before #19 but I guess having this in an issue would be fitting until we have resolved this issue.

pooler problem [:pooler_app.start(:normal, [])] not found

I am using the {:riak, "~> 1.0"} when I run this on elixir 1.4.0 and erlang otp 19.3 I am getting the follwing error

** (Mix) Could not start application pooler: exited in: :pooler_app.start(:normal, [])
    ** (EXIT) an exception was raised:
        ** (UndefinedFunctionError) function :pooler_app.start/2 is undefined (module :pooler_app is not available)
            (pooler) :pooler_app.start(:normal, [])
            (kernel) application_master.erl:273: :application_master.start_it_old/4

If you need more info let me know.

Pooler can't find riak group when used in umbrella app.

Hi all, I am using riak-elixir-client in an umbrella app and has configured pooler in my config, this works all fine and I able to save and retrieve data from local riak instance as long as I work within this app.
However, when I try to use this app from another app within my umbrella project , I get below error on saving :

** (FunctionClauseError) no function clause matching in Riak.update/5 stacktrace: lib/riak.ex:263: Riak.update({:error_no_group, :riak}, {:map, [], [{{"category_fields", :map}, {:map, [], [{{"sender_id", :register} , {:register, "", "archer"}}], [], :undefined}}, {{"category_type", :register}, {:register, "", "invitation_request"}}, {{"notification_id" , :register}, {:register, "", "rin_invitation_request_77353b64-fef4-11e6-a04d-c4b301c08693"}}, {{"user_id", :register}, {:register, "", "ri n"}}], [], :undefined}, "maps", "notifications", "rin_invitation_request_77353b64-fef4-11e6-a04d-c4b301c08693") lib/riak.ex:263: Riak.update/4 lib/notification_cordinator.ex:12: Paraaz.NotificationCordinator.save/3 test/controllers/notification_controller_test.exs:20: (test)

it seems somehow pooler is not able to find riak group.

Code is here : https://github.com/nav301186/rumuk/tree/develop

Not handling growing concurrency past `init_count` very well

Hi there, I'm using Riak in a system that potentially requires high concurrency and have run into an exception that's hard to handle. It looks like this:

21:03:45.777 [error] Process #PID<0.192.0> raised an exception
** (FunctionClauseError) no function clause matching in Riak.find/4
    (riak) lib/riak.ex:301: Riak.find(:error_no_members, :error_no_members, "abc", "def")
    (riak) lib/riak.ex:301: Riak.find/3
    (riak) lib/riak.ex:282: Riak.find/2

I set up a test repo with instructions to demonstrate: https://github.com/MaxPower15/riak_pooler_issue

I poked around myself with pooler, hoping to maybe get a PR fix, but I don't think I'm familiar enough with the underlying code to really do it correctly. I hope this demo helps though!

Document Riak.find/3 vs Riak.find/4 better

There is confusion between bucket types and bucket datatypes that are crdt. I had some issues using strongly consistent types. I suggest saying that Riak.find/3 works with types while Riak.find/4 works with crdt datatypes or redoing the abstraction.

http://docs.basho.com/riak/kv/2.2.3/developing/usage/bucket-types/

# [Already started] 3 node riak cluster
riak-admin bucket-type create strongly_consistent \ 
  '{"props":{"consistent":true}}'
riak-admin bucket-type activate strongly_consistent
# Test if the cluster is working
curl -v 172.17.0.2:8098/types/strongly_consistent/buckets/test/keys/hello \
  -X PUT \
  -H "Content-type: text/plain" \
  -d "world"
curl -v 172.17.0.2:8098/types/strongly_consistent/buckets/test/keys/hello
# Elixir
{:ok, pid} = Riak.Connection.start_link('172.17.0.2', 8087)
{:ok, #PID<0.399.0>}
iex(3)> Riak.find(pid, "strongly_consistent", "test", "hello")  
nil
iex(14)> Riak.find(pid, {"strongly_consistent", "test"}, "hello")
%Riak.Object{bucket: {"strongly_consistent", "test"},
 content_type: 'text/plain', data: "world", key: "hello",
 metadata: {:dict, 3, 16, 16, 8, 80, 48,
  {[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []},
  {{[], [], [], [], [], [], [], [], [], [],
    [["content-type", 116, 101, 120, 116, 47, 112, 108, 97, 105, 110],
     ["X-Riak-VTag", 51, 66, 120, 87, 50, 84, 53, 68, 117, 97, 117, 71, 72,
      85, 89, 101, 80, 55, 65, 109, 109, 118]], [], [],
    [["X-Riak-Last-Modified" | {1492, 865281, 773593}]], [], []}}},
 type: "strongly_consistent",
 vclock: <<107, 206, 97, 96, 96, 96, 204, 96, 74, 97, 96, 73, 45, 78, 45,
   204, 96, 202, 99, 101, 112, 0, 9, 1, 105, 198, 179, 249, 151, 248, 178,
   0>>}

:tcp_closed

I have installed Riak via docker:
http://basho.com/posts/technical/riak-quick-start-with-docker/

Connecting worked fine:
{:ok, pid} = Riak.Connection.start_link('192.168.99.100', 32774)

However, putting a user has failed, here is my code:

def store_to_riak do
  {:ok, pid} = Riak.Connection.start_link('192.168.99.100', 32774)
  IO.inspect pid
  o = Riak.Object.create(bucket: "user", key: "my_key", data: "Han Solo")
  IO.inspect o
  Riak.put(pid, o)
end

The result:

iex(1)> App.LocationController.store_to_riak
#PID<0.314.0>
%Riak.Object{bucket: "user", content_type: 'application/json', data: "Han Solo",
 key: "my_key",
 metadata: {:dict, 1, 16, 16, 8, 80, 48,
  {[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []},
  {{[], [], [], [], [], [], [], [], [], [],
    [["content-type", 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47,
      106, 115, 111, 110]], [], [], [], [], []}}}, type: :undefined,
 vclock: :undefined}
nil
** (EXIT from #PID<0.312.0>) :disconnected

Interactive Elixir (1.2.5) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> [error] GenServer #PID<0.314.0> terminating
** (stop) :disconnected
Last message: {:tcp_closed, #Port<0.12186>}
State: {:state, '192.168.99.100', 32774, false, false, :undefined, false, :gen_tcp, :undefined, {[], []}, 1, [], :infinity, :undefined, :undefined, :undefined, :undefined, [], 100}

But, sense I am new to Riak, I don't know how to handle such errors, if you can help that would be great

has_key? won't work for CRDT maps

So, when using CRDT maps with register types, the underlying call has to be, for instance:

:riakc_map.is_key({<<"status">>, :register}, map)

and the in map.ex, line 68, the guard clause prevents that; I've removed the "and is_binary(key)" and it now works such that this gives the correct response:

Riak.CRDT.Map.has_key?(obj, {"status", :register})

But I'm sure that there is some checking that could still be done on the key, I just don't know what it is.

Fails for Erlang/OTP 18

mix deps.get

Compiling src/pooler.erl failed:

src/pooler.erl:209: erlang:now/0: Deprecated BIF. See the "Time and Time Correction in Erlang" chapter of the ERTS User's Guide for more information.

ERROR: compile failed while processing /Users/xxx/Projects/xxx/app/deps/pooler: rebar_abort
** (Mix) Could not compile dependency :pooler, "/Users/xxx/.mix/rebar" command failed. You can recompile this dependency with "mix deps.compile pooler", update it with "mix deps.update pooler" or clean it with "mix deps.clean pooler"

Deleting value for nested CRDT SET

Hi,
I have a crdt Map which contains a set as shown below
{:map, [{{"images", :set}, ["1.png"]}, {{"user_id", :register}, "nav"}], [], [], │.***************delete *********** <<131, 108, 0, 0, 0, 1, 104, 2, 109, 0, 0, 0, 12, 35, 9, 254, 249, 42, 2, 65, │"/var/folders/2r/1vc2nqzx5d95vg57rrs1v5r40000gn/T//briefly-1526/briefly-112687-902595-2/rin_1/image.jpeg" 179, 0, 1, 17, 139, 97, 5, 106>>}

I want to delete values from this nested set (named "images"). somehow I am not able to achieve this.

I tried updated in the crdt map with a new set altogether but it does not seems to be working.
set = Set.new updated_crdt = Map.put(crdt, "images", set) Riak.update(u_crdt, "media", "media", "nav")
I get below error:
{:error, :unmodified}

Any help is much appreciated.

tests fail if riak buckets have not already been created.

Description:
Tests fail if riak buckets not already created.
The README.md file refers to creating the buckets as an example but I
can't see where it says it's explicitly required to pass the tests.

Proposal:
Explicitly state under Tests header in docs that the buckets must be created.

Riak client disconnects after a couple of minutes

Riak-elixir-client hangs after sitting idle for a couple of minutes, and I have to restart Phoenix in order to re-establish a connection to the Riak cluster. I'm new to Riak, so my configuration may be wrong, but it looks correct, according to the docs. If my configuration is correct, does anyone have a workaround for this issue?

Here is a little more details and also my configuration settings:

I am running riak-elixir-client in a Phoenix 1.2.0 application on Erlang 19 and Elixir 1.3.2. My code is running on OS X locally, and several Microsoft Azure instances of Ubuntu 14.04 LTS (I'm seeing the same problem in both places). When I first start the Phoenix server using

    iex -S mix phoenix.server

I can pull data from Riak successfully. For example,

    Riak.find("person", "33")

returns the correct data.

However, if I let the server run idle for 2 or 3 minutes, calls to Riak will hang, and eventually end with this error:

[error] GenServer #PID<0.297.0> terminating
** (stop) :disconnected
Last message: {:req_timeout, #Reference<0.0.6.2919>}
State: {:state, 'dev-riak-5.example.com', 8087, false, false, :undefined, false, :gen_tcp, :undefined, {[], []}, 1, [], :infinity, :undefined, :undefined, :undefined, :undefined, [], 100}

I then have to either restart Phoenix, or open :observer.start, and kill all the pooler supervisors in order to re-establish a connection to my Riak cluster.

Here are my relevant config settings:

dev.exs and prod.exs pooler configurations:
config :pooler, pools:
[
[
name: :dev_riak_1,
group: :riak,
max_count: 20,
init_count: 5,
start_mfa: { Riak.Connection, :start_link, ['dev-riak-1.example.com', 8087] }
],
[
name: :dev_riak_2,
group: :riak,
max_count: 20,
init_count: 5,
start_mfa: { Riak.Connection, :start_link, ['dev-riak-2.example.com', 8087] }
],
[
name: :dev_riak_3,
group: :riak,
max_count: 20,
init_count: 5,
start_mfa: { Riak.Connection, :start_link, ['dev-riak-3.example.com', 8087] }
],
[
name: :dev_riak_4,
group: :riak,
max_count: 20,
init_count: 5,
start_mfa: { Riak.Connection, :start_link, ['dev-riak-4.example.com', 8087] }
],
[
name: :dev_riak_5,
group: :riak,
max_count: 20,
init_count: 5,
start_mfa: { Riak.Connection, :start_link, ['dev-riak-5.example.com', 8087] }
]
]

mix.exs:
def application do
[mod: {Skip, []},
applications: [
:cowboy,
:gettext,
:httpotion,
:logger,
:phoenix,
:phoenix_html,
:phoenix_pubsub,
:phoenix_slime,
:riak
]
]
end
defp deps do
[
{:cowboy, "> 1.0"},
{:exrm, "
> 1.0"},
{:gettext, "> 0.11"},
{:httpotion, "
> 3.0.0"},
{:phoenix, "> 1.2.0"},
{:phoenix_html, "
> 2.6"},
{:phoenix_live_reload, "> 1.0", only: :dev},
{:phoenix_pubsub, "
> 1.0"},
{:phoenix_slime, "> 0.7.0"},
{:relx, github: "erlware/relx", override: true},
{:riak, "
> 1.0"}
]
end

Thanks for any future answers.

Non-existing key cannot be handled in CRDT.Map

When I try to get a non-existing key from a map I got a function clause caused by the underlying orddict.

iex(4)> Riak.CRDT.Map.get(v(2), :counter, "free")
** (FunctionClauseError) no function clause matching in :orddict.fetch/2
    (stdlib) orddict.erl:80: :orddict.fetch({"free", :counter}, [{{"values", :set}, ["101"]}])

Ok, I tried with has_key?

** (FunctionClauseError) no function clause matching in :orddict.fetch/2
     stacktrace:
       (stdlib) orddict.erl:80: :orddict.fetch({"values", :set}, [])

I would be comfortable if get/3 results in nil in case of non-existing key. And obviously has_key?/2 should give false.
Using riak-1.0.0.

Riak module's update and find need clarity

The update and find functions in the Riak module are confusing - it is not clear whether you're dealing with a datatype or regular object. It would probably make more sense to move the datatype specific functions into Riak.CRDT. Additionally it seems appropriate to move Riak.CRDT to Riak.Datatype, and also move some of the Riak module functions to Riak.KV or Riak.Object to clear things up.

Thoughts on this naming change?

Create bucket?

As for the command:

o = Riak.Object.create(bucket: "user", key: "my_key", data: "Han Solo")
Riak.put(pid, o)

Do I have to create bucket "user" before? or does this command creates the bucket if it does not exist?

Compilation issue - undefined function defrecord/3

Following the README (added to deps list, mix deps.get, mix), I get the following compile error:

== Compilation error on file lib/riak/object.ex ==
** (CompileError) lib/riak/object.ex:80: undefined function defrecord/3
    (stdlib) lists.erl:1352: :lists.mapfoldl/3
    (stdlib) lists.erl:1353: :lists.mapfoldl/3
    (elixir) src/elixir_exp.erl:49: :elixir_exp.expand/2
    (elixir) src/elixir.erl:206: :elixir.quoted_to_erl/3
    (elixir) src/elixir_lexical.erl:17: :elixir_lexical.run/3

==> riak-elixir-client
could not compile dependency riak-elixir-client, mix compile failed. You can recompile this dependency with `mix deps.compile riak-elixir-client` or update it with `mix deps.update riak-elixir-client`

Erlang/OTP 17 [erts-6.1] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Elixir 0.15.1

How i can get a first key?

Hi!
I need get only one key from storage by moment. Exist methods like next/first or riak not implemented this functionality?

[feature request] pool connection delegate fn

When I create a pool, I'd like to be able to pass in a function that is periodically called that returns a set of hosts for the pool to use.

The use case is, that I would implement a function that talks to the riak-mesos endpoints url to find the current set of nodes that the cluster is running. This would obviate the need for talking through a proxy for long running services, w/o special casing how that node list is derived.

Riak.Object.create inconsitent with Riak.Object.create(args) using indexes

Hi guys,

I found some inconsistent behaviour between the Riak.Object.create(args) && Riak.Object.create functions.

object = Riak.Object.create
object = Riak.Object.put_index(object, {:binary_index, "test"}, ["test"])

Will result in:

** (ErlangError) erlang error: {:badrecord, :dict}
    (stdlib) dict.erl:390: :dict.get_slot/2
    (stdlib) dict.erl:142: :dict.find/2

While:

object = Riak.Object.create(%{bucket: "bla"})                   
Riak.Object.put_index(object, {:binary_index, "test"}, ["test"])

Will just work:

%Riak.Object{bucket: "bla", content_type: 'application/json', data: nil,
 key: :undefined,
 metadata: {:dict, 2, 16, 16, 8, 80, 48,
  {[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []},
  {{[], [], [], [], [], [], [], [], [], [],
    [["content-type", 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47,
      106, 115, 111, 110]], [["index", {"test_bin", "test"}]], [], [], [],
    []}}}, type: :undefined, vclock: :undefined}

If you want me to write a test for it, let me know.

No way to delete with data types?

I am trying to delete a map from a bucket called "users" of type "maps". It does not appear that this capability is supported. According to the HTTP docs something like DELETE /types/<type>/buckets/<bucket>/keys/<key> is needed. Am I missing something or is this not supported?

{:ok, riak} = Riak.Connection.start_link('127.0.0.1', 8087) 
m = Riak.find(riak, "maps", "users", "1d1cf86e-e7c8-11e4-b576-542696dfbfa9") |> Riak.CRDT.Map.value
:ok = Riak.delete(riak, "users", "1d1cf86e-e7c8-11e4-b576-542696dfbfa9") # Doesn't delete it
Riak.delete(riak, "maps", "users", "1d1cf86e-e7c8-11e4-b576-542696dfbfa9") # No such function - errors out

On bucket.save vclock does not get set in return object

Hi guys,

Saving an object using Riak.put will give back an object. This object does not have a vclock, so a sibbling will be created when you update it afterwards.

object = Riak.put object # created
object = %{object | data: "Updated"} # update created record
Riak.put object 

There is a workaround to prevent the sibbling being created, that is to fetch the record in between and save it then. However, that is an extra query to the database that I would like to prevent.

The object should have an added vclock. I could contribute writing a test for this, but I don't really get where the object is returned. Probably from the erlang library.

Search argument names are incorrect

riakc function signature is this:

create_search_index(Pid::pid(), Index::binary()) -> ok

riak elixir client has

put(pid, bucket)

This tripped me up when I first came across it, since I thought it was an interface for adding a search index to a bucket's props.

I propose we change the arguments to use index.

defpool with guards

@edgurgel: I don't think the guard for is_pid(pid) is working for guards that are using defpool.

defpool is a macro which happens at compile time. That means beam can't (may not be able to) prevent function calls with pid variable actually being an atom :error_no_members. I suppose the question is, does the guard on Riak.find work even when using the defpool macro?

Any thoughts on how to better handle this when application code calls .find?

See this trace for reference:

GenServer #PID<0.842.10> terminating
** (FunctionClauseError) no function clause matching in :riak_dt_pb.e_type_bytes/2
    src/riak_dt_pb.erl:905: :riak_dt_pb.e_type_bytes(:error_no_members, <<10, 18, 112, 114, 111, 100, 31, 114, 101, 100, 117, 99, 101, 114, 95, 115, 116, 97, 116, 101, 18, 56, 115, 116, 111, 114, 101, 31, 97, 99, 56, 51, 54, 102, 50, 55, 45, 50, 55, 102, 51, 45, 52, 100, 49, 49, 45, 56, 101, 97, ...>>)
    src/riak_dt_pb.erl:219: :riak_dt_pb.e_msg_dtfetchreq/3
    src/riak_pb_codec.erl:94: :riak_pb_codec.encode/1
    (riakc) src/riakc_pb_socket.erl:2331: :riakc_pb_socket.encode_request_message/1
    (riakc) src/riakc_pb_socket.erl:2303: :riakc_pb_socket.send_request/2

    (stdlib) gen_server.erl:665: :gen_server.handle_msg/6
Last message (from #PID<0.504.10>): {:req, {:dtfetchreq, <<112, 114, 111, 100, 31, 114, 101, 100, 117, 99, 101, 114, 95, 115, 116, 97, 116, 101>>, <<115, 116, 111, 114, 101, 31, 97, 99, 56, 51, 54, 102, 50, 55, 45, 50, 55, 102, 51, 45, 52, 100, 49, 49, 45, 56, 101, 97, 101, 45, 50, 98, 102, 98, 52, 55, 100, 56, 53, 48, 51, 48, 31, 115, 101, ...>>, :error_no_members, :undefined, :undefined, :undefined, :undefined, :undefined, :undefined, :undefined, true}, 60000}
State: {:state, 'some.loadbalancer.com', 8087, false, false, #Port<0.102632>, false, :gen_tcp, :undefined, {[], []}, 1, [], :infinity, :undefined, :undefined, :undefined, :undefined, [], 100}
Client #PID<0.504.10> is alive
    (stdlib) gen.erl:169: :gen.do_call/4
    (stdlib) gen_server.erl:210: :gen_server.call/3
    (riak) lib/riak.ex:305: Riak.find/4
    (riak) lib/riak.ex:304: Riak.find/3
    (riak) lib/riak.ex:285: Riak.find/2

conflict resolution ?

I may be mistaken, but I can't see an obvious place to put a client callback to handle conflict resolution. Is this functionality implemented?

Can't compile the example app exploriak

When I try to compile the example app exploriak, I get the error message

rm -f exploriak
mix deps.get
Running dependency resolution...
* Getting riak (Hex package)
  Checking package (https://repo.hex.pm/tarballs/riak-1.0.0.tar)
  Using locally cached package
* Getting linguist (Hex package)
  Checking package (https://repo.hex.pm/tarballs/linguist-0.1.5.tar)
  Using locally cached package
* Getting pooler (Hex package)
  Checking package (https://repo.hex.pm/tarballs/pooler-1.5.0.tar)
  Using locally cached package
* Getting riakc (Hex package)
  Checking package (https://repo.hex.pm/tarballs/riakc-2.1.1.tar)
  Using locally cached package
* Getting riak_pb (Hex package)
  Checking package (https://repo.hex.pm/tarballs/riak_pb-2.1.0.tar)
  Using locally cached package
* Getting hamcrest (Hex package)
  Checking package (https://repo.hex.pm/tarballs/hamcrest-0.1.2.tar)
  Using locally cached package
* Getting protobuffs (Hex package)
  Checking package (https://repo.hex.pm/tarballs/protobuffs-0.8.2.tar)
  Using locally cached package
* Getting meck (Hex package)
  Checking package (https://repo.hex.pm/tarballs/meck-0.8.3.tar)
  Using locally cached package
mix escript.build
==> pooler (compile)
Compiled src/pooler_starter.erl
Compiled src/pooler_starter_sup.erl
Compiled src/pooler_sup.erl
Compiled src/pooler_pooled_worker_sup.erl
Compiled src/pooler_config.erl
Compiled src/pooler_app.erl
Compiled src/pooler_pool_sup.erl
Compiled src/pooler.erl
==> hamcrest (compile)
Compiled src/hamcrest.erl
Compiled src/hamcrest_term.erl
Compiled src/hamcrest_matchers.erl
==> hamcrest (post_compile)
==> linguist
Compiling 3 files (.ex)
warning: parentheses are required when piping into a function call. For example:

    foo 1 |> bar 2 |> baz 3

is ambiguous and should be written as

    foo(1) |> bar(2) |> baz(3)

Ambiguous pipe found at:
  lib/linguist/compiler.ex:72

Generated linguist app
==> meck (compile)
Compiled src/meck_util.erl
Compiled src/meck_ret_spec.erl
Compiled src/meck_proc.erl
Compiled src/meck_matcher.erl
Compiled src/meck_history.erl
Compiled src/meck_expect.erl
Compiled src/meck_cover.erl
Compiled src/meck_code.erl
Compiled src/meck_args_matcher.erl
Compiled src/meck_code_gen.erl
Compiled src/meck.erl
==> protobuffs (compile)
Compiled src/protobuffs_file.erl
Compiled src/protobuffs_scanner.erl
Compiled src/protobuffs_parser.erl
Compiled src/protobuffs_cli.erl
Compiled src/pokemon_pb.erl
Compiled src/protobuffs.erl
Compiled src/protobuffs_compile.erl
==> riak_pb (pre_compile)
==> riak_pb (compile)
Compiling src/riak_yokozuna.proto

=INFO REPORT==== 2-Jan-2017::13:33:45 ===
Writing header file to "riak_yokozuna_pb.hrl"

=INFO REPORT==== 2-Jan-2017::13:33:45 ===
Writing beam file to "riak_yokozuna_pb.beam"
Compiling src/riak_search.proto

=INFO REPORT==== 2-Jan-2017::13:33:45 ===
Writing header file to "riak_search_pb.hrl"

=INFO REPORT==== 2-Jan-2017::13:33:45 ===
Writing beam file to "riak_search_pb.beam"
Compiling src/riak_kv.proto

=INFO REPORT==== 2-Jan-2017::13:33:45 ===
Writing header file to "riak_kv_pb.hrl"

=INFO REPORT==== 2-Jan-2017::13:33:46 ===
Writing beam file to "riak_kv_pb.beam"
Compiling src/riak_dt.proto

=INFO REPORT==== 2-Jan-2017::13:33:46 ===
Writing header file to "riak_dt_pb.hrl"

=INFO REPORT==== 2-Jan-2017::13:33:46 ===
Writing beam file to "riak_dt_pb.beam"
Compiling src/riak.proto

=INFO REPORT==== 2-Jan-2017::13:33:46 ===
Writing header file to "riak_pb.hrl"

=INFO REPORT==== 2-Jan-2017::13:33:46 ===
Writing beam file to "riak_pb.beam"
Compiled src/riak_pb_kv_codec.erl
Compiled src/riak_pb_codec.erl
Compiled src/riak_pb_search_codec.erl
Compiled src/riak_pb_messages.erl
Compiled src/riak_pb_dt_codec.erl
==> riakc (compile)
ERROR: OTP release 19 does not match required regex R15|R16|17|18
ERROR: compile failed while processing /Users/schaary/code/riak-elixir-client/examples/exploriak/deps/riakc: rebar_abort
==> exploriak
** (Mix) Could not compile dependency :riakc, "/Users/schaary/.mix/rebar compile skip_deps=true deps_dir="/Users/schaary/code/riak-elixir-client/examples/exploriak/_build/dev/lib"" command failed. You can recompile this dependency with "mix deps.compile riakc", update it with "mix deps.update riakc" or clean it with "mix deps.clean riakc"
make: *** [all] Error 1```

I run Elixir v1.3.4 with Erlang/OTP 19 on macOS 10.12.2

Riak.Search.query is reported not defined if taking the pooler route

This is a bit weird. I got two projects. One is an app build with mix phoenix.new, and the other is a basic mix new riak_playground with the riak-elixir-client added to the mix file dependencies and started in the applications—I use the bare bones one to test out stuff with Riak and the Elixir client.

Both apps uses the same pooler configuration. Both apps seem to be able to do most commands, such as Riak.update, without specifying the pid—but for some reason Riak.Search.query seems to behave differently in my phoenix app. If I type the following In iex:

iex(1)> Riak.Search.query("users", "_yz_rb:profile AND email_register:[email protected]", rows: 1)

It will work just fine, and print the search result. When the same line is copy-pasted into the Phoenix-app it will fail with the following error:

[error] #PID<0.1102.0> running MyPhoenixApp.Endpoint terminated
Server: localhost:4000 (http)
Request: POST /auth/identity/callback
** (exit) an exception was raised:
    ** (FunctionClauseError) no function clause matching in Riak.Search.query/4
        (riak) lib/riak/search.ex:4: Riak.Search.query("users", "_yz_rb:profile AND email_register:[email protected]", [rows: 1], [])
[... lines omitted ...]

I have tried debugging outputting the result of Kernel.function_exported?(Riak.Search, :query, 4) which says the function is exported just fine (true), and ensured that the Riak application has been started with Application.ensure_started(:riak) which reports :ok.

The error seems to go away if I set some pid as the first argument (a pid such as self) to the Riak.Search.query, so something might be up with pooler or the defpool-macro, but I am just guessing.

Flag disable erroring with :context_required

When trying to disable a flag there is a :context_required error.

Erlang/OTP 17 [erts-6.4] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Interactive Elixir (1.0.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> alias Riak.CRDT.Flag
nil
iex(2)> f1 = Flag.new
{:flag, false, :undefined, :undefined}
iex(3)> f2 = Flag.new |> Flag.enable 
{:flag, false, :enable, :undefined}
iex(4)> f3 = Flag.new |> Flag.disable
** (throw) :context_required
    (riakc) src/riakc_flag.erl:102: :riakc_flag.disable/1
iex(5)> Flag.disable(f2)
** (throw) :context_required
    (riakc) src/riakc_flag.erl:102: :riakc_flag.disable/1

Update fails after deleting a key from a Map

I'm getting the following issue updating a map after deleting a key. This might be a problem with riak_pb, but haven't got to the bottom of it yet, so thought I'd create an issue here just in case.

iex(1)> alias Riak.CRDT.Map
Riak.CRDT.Map
iex(2)> map = Riak.find("maps", "maps", "somekey") |> Map.delete("reg")
{:map, [{{"reg", :register}, "some value"}], [], ["reg"],
 <<131, 108, 0, 0, 0, 1, 104, 2, 109, 0, 0, 0, 12, 35, 9, 254, 249, 206, 13, 20,
   223, 0, 0, 0, 1, 97, 1, 106>>}
iex(3)> map |> Riak.update("maps", "maps", "somekey")
** (FunctionClauseError) no function clause matching in :riak_pb_dt_codec.encode_map_field/2
            src/riak_pb_dt_codec.erl:112: :riak_pb_dt_codec.encode_map_field("reg", [])
            src/riak_pb_dt_codec.erl:388: :riak_pb_dt_codec.encode_map_op_update/2
            src/riak_pb_dt_codec.erl:425: :riak_pb_dt_codec.encode_operation/2
            src/riak_pb_dt_codec.erl:448: :riak_pb_dt_codec.encode_update_request/4
    (riakc) src/riakc_pb_socket.erl:1248: :riakc_pb_socket.update_type/5
     (riak) lib/riak.ex:262: Riak.update/4

Unable to delete a key/value pair from a map

I'm trying to delete a register from a map, also trying to delete a counter from a map.

Code:
map = Riak.find("maps", "test_maps", "test1")
Riak.CRDT.Map.delete(map, "test_reg")
|> Riak.update("maps", "test_maps", "test1")

Error:
** (exit) an exception was raised:
** (FunctionClauseError) no function clause matching in :riak_pb_dt_codec.encode_map_field/1
(riak_pb 2.3.2) src/riak_pb_dt_codec.erl:116: :riak_pb_dt_codec.encode_map_field("reg1")
(riak_pb 2.3.2) src/riak_pb_dt_codec.erl:428: :riak_pb_dt_codec.encode_map_op_update/2
(riak_pb 2.3.2) src/riak_pb_dt_codec.erl:473: :riak_pb_dt_codec.encode_operation/2
(riak_pb 2.3.2) src/riak_pb_dt_codec.erl:500: :riak_pb_dt_codec.encode_update_request/4
(riakc 2.5.3) src/riakc_pb_socket.erl:1246: :riakc_pb_socket.update_type/5
(riak 1.1.6) lib/riak.ex:264: Riak.update/4

I tried many different ways to delete a key/value pair from a map, but unable to do so.
Am I doing something wrong?
I am able to update a map when replacing a key/value pair by using the put function instead of the delete.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.