├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── THANKS
├── config
└── config.exs
├── doc
├── 404.html
├── Riak.Bucket.Type.html
├── Riak.Bucket.html
├── Riak.CRDT.Counter.html
├── Riak.CRDT.Flag.html
├── Riak.CRDT.HyperLogLog.html
├── Riak.CRDT.Map.html
├── Riak.CRDT.Register.html
├── Riak.CRDT.Set.html
├── Riak.CRDT.html
├── Riak.Connection.html
├── Riak.Index.html
├── Riak.Mapred.Bucket.html
├── Riak.Mapred.html
├── Riak.Object.html
├── Riak.Pool.html
├── Riak.Search.Index.html
├── Riak.Search.Schema.html
├── Riak.Search.html
├── Riak.Timeseries.html
├── Riak.html
├── api-reference.html
├── dist
│ ├── app-268c9dc719.css
│ ├── app-4988ddd06b.js
│ ├── app-4988ddd06b.js.map
│ └── sidebar_items-8bfd31e79d.js
├── fonts
│ ├── icomoon.eot
│ ├── icomoon.svg
│ ├── icomoon.ttf
│ └── icomoon.woff
└── index.html
├── examples
└── exploriak
│ ├── .gitignore
│ ├── Makefile
│ ├── README.md
│ ├── exploriak
│ ├── lib
│ └── exploriak.ex
│ ├── mix.exs
│ ├── mix.lock
│ └── test
│ ├── exploriak_test.exs
│ └── test_helper.exs
├── lib
├── riak.ex
└── riak
│ ├── bucket.ex
│ ├── connection.ex
│ ├── crdt.ex
│ ├── crdt
│ ├── counter.ex
│ ├── flag.ex
│ ├── hll.ex
│ ├── map.ex
│ ├── register.ex
│ └── set.ex
│ ├── index.ex
│ ├── map_reduce.ex
│ ├── object.ex
│ ├── pool.ex
│ ├── search.ex
│ └── timeseries.ex
├── mix.exs
├── mix.lock
└── test
├── bucket_test.exs
├── crdt_counter_test.exs
├── crdt_test.exs
├── flag_test.exs
├── hll_test.exs
├── map_test.exs
├── pool_test.exs
├── riak_test.exs
├── set_test.exs
├── test_helper.exs
└── timeseries_test.exs
/.gitignore:
--------------------------------------------------------------------------------
1 | _build
2 | deps
3 | ebin
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: elixir
2 | elixir:
3 | - 1.4.1
4 | notifications:
5 | recipients:
6 | - drew@kerrigan.io
7 | otp_release:
8 | - 19.2
9 | env:
10 | - MIX_ENV=test
11 | services:
12 | - riak
13 | script: mix test --exclude riak2 --exclude riakts
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2014-2015 Drew Kerrigan
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
15 | All of the files in this project are under the project-wide license
16 | unless they are otherwise marked.
17 | Apache License
18 | Version 2.0, January 2004
19 | http://www.apache.org/licenses/
20 |
21 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
22 |
23 | 1. Definitions.
24 |
25 | "License" shall mean the terms and conditions for use, reproduction,
26 | and distribution as defined by Sections 1 through 9 of this document.
27 |
28 | "Licensor" shall mean the copyright owner or entity authorized by
29 | the copyright owner that is granting the License.
30 |
31 | "Legal Entity" shall mean the union of the acting entity and all
32 | other entities that control, are controlled by, or are under common
33 | control with that entity. For the purposes of this definition,
34 | "control" means (i) the power, direct or indirect, to cause the
35 | direction or management of such entity, whether by contract or
36 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
37 | outstanding shares, or (iii) beneficial ownership of such entity.
38 |
39 | "You" (or "Your") shall mean an individual or Legal Entity
40 | exercising permissions granted by this License.
41 |
42 | "Source" form shall mean the preferred form for making modifications,
43 | including but not limited to software source code, documentation
44 | source, and configuration files.
45 |
46 | "Object" form shall mean any form resulting from mechanical
47 | transformation or translation of a Source form, including but
48 | not limited to compiled object code, generated documentation,
49 | and conversions to other media types.
50 |
51 | "Work" shall mean the work of authorship, whether in Source or
52 | Object form, made available under the License, as indicated by a
53 | copyright notice that is included in or attached to the work
54 | (an example is provided in the Appendix below).
55 |
56 | "Derivative Works" shall mean any work, whether in Source or Object
57 | form, that is based on (or derived from) the Work and for which the
58 | editorial revisions, annotations, elaborations, or other modifications
59 | represent, as a whole, an original work of authorship. For the purposes
60 | of this License, Derivative Works shall not include works that remain
61 | separable from, or merely link (or bind by name) to the interfaces of,
62 | the Work and Derivative Works thereof.
63 |
64 | "Contribution" shall mean any work of authorship, including
65 | the original version of the Work and any modifications or additions
66 | to that Work or Derivative Works thereof, that is intentionally
67 | submitted to Licensor for inclusion in the Work by the copyright owner
68 | or by an individual or Legal Entity authorized to submit on behalf of
69 | the copyright owner. For the purposes of this definition, "submitted"
70 | means any form of electronic, verbal, or written communication sent
71 | to the Licensor or its representatives, including but not limited to
72 | communication on electronic mailing lists, source code control systems,
73 | and issue tracking systems that are managed by, or on behalf of, the
74 | Licensor for the purpose of discussing and improving the Work, but
75 | excluding communication that is conspicuously marked or otherwise
76 | designated in writing by the copyright owner as "Not a Contribution."
77 |
78 | "Contributor" shall mean Licensor and any individual or Legal Entity
79 | on behalf of whom a Contribution has been received by Licensor and
80 | subsequently incorporated within the Work.
81 |
82 | 2. Grant of Copyright License. Subject to the terms and conditions of
83 | this License, each Contributor hereby grants to You a perpetual,
84 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
85 | copyright license to reproduce, prepare Derivative Works of,
86 | publicly display, publicly perform, sublicense, and distribute the
87 | Work and such Derivative Works in Source or Object form.
88 |
89 | 3. Grant of Patent License. Subject to the terms and conditions of
90 | this License, each Contributor hereby grants to You a perpetual,
91 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
92 | (except as stated in this section) patent license to make, have made,
93 | use, offer to sell, sell, import, and otherwise transfer the Work,
94 | where such license applies only to those patent claims licensable
95 | by such Contributor that are necessarily infringed by their
96 | Contribution(s) alone or by combination of their Contribution(s)
97 | with the Work to which such Contribution(s) was submitted. If You
98 | institute patent litigation against any entity (including a
99 | cross-claim or counterclaim in a lawsuit) alleging that the Work
100 | or a Contribution incorporated within the Work constitutes direct
101 | or contributory patent infringement, then any patent licenses
102 | granted to You under this License for that Work shall terminate
103 | as of the date such litigation is filed.
104 |
105 | 4. Redistribution. You may reproduce and distribute copies of the
106 | Work or Derivative Works thereof in any medium, with or without
107 | modifications, and in Source or Object form, provided that You
108 | meet the following conditions:
109 |
110 | (a) You must give any other recipients of the Work or
111 | Derivative Works a copy of this License; and
112 |
113 | (b) You must cause any modified files to carry prominent notices
114 | stating that You changed the files; and
115 |
116 | (c) You must retain, in the Source form of any Derivative Works
117 | that You distribute, all copyright, patent, trademark, and
118 | attribution notices from the Source form of the Work,
119 | excluding those notices that do not pertain to any part of
120 | the Derivative Works; and
121 |
122 | (d) If the Work includes a "NOTICE" text file as part of its
123 | distribution, then any Derivative Works that You distribute must
124 | include a readable copy of the attribution notices contained
125 | within such NOTICE file, excluding those notices that do not
126 | pertain to any part of the Derivative Works, in at least one
127 | of the following places: within a NOTICE text file distributed
128 | as part of the Derivative Works; within the Source form or
129 | documentation, if provided along with the Derivative Works; or,
130 | within a display generated by the Derivative Works, if and
131 | wherever such third-party notices normally appear. The contents
132 | of the NOTICE file are for informational purposes only and
133 | do not modify the License. You may add Your own attribution
134 | notices within Derivative Works that You distribute, alongside
135 | or as an addendum to the NOTICE text from the Work, provided
136 | that such additional attribution notices cannot be construed
137 | as modifying the License.
138 |
139 | You may add Your own copyright statement to Your modifications and
140 | may provide additional or different license terms and conditions
141 | for use, reproduction, or distribution of Your modifications, or
142 | for any such Derivative Works as a whole, provided Your use,
143 | reproduction, and distribution of the Work otherwise complies with
144 | the conditions stated in this License.
145 |
146 | 5. Submission of Contributions. Unless You explicitly state otherwise,
147 | any Contribution intentionally submitted for inclusion in the Work
148 | by You to the Licensor shall be under the terms and conditions of
149 | this License, without any additional terms or conditions.
150 | Notwithstanding the above, nothing herein shall supersede or modify
151 | the terms of any separate license agreement you may have executed
152 | with Licensor regarding such Contributions.
153 |
154 | 6. Trademarks. This License does not grant permission to use the trade
155 | names, trademarks, service marks, or product names of the Licensor,
156 | except as required for reasonable and customary use in describing the
157 | origin of the Work and reproducing the content of the NOTICE file.
158 |
159 | 7. Disclaimer of Warranty. Unless required by applicable law or
160 | agreed to in writing, Licensor provides the Work (and each
161 | Contributor provides its Contributions) on an "AS IS" BASIS,
162 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
163 | implied, including, without limitation, any warranties or conditions
164 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
165 | PARTICULAR PURPOSE. You are solely responsible for determining the
166 | appropriateness of using or redistributing the Work and assume any
167 | risks associated with Your exercise of permissions under this License.
168 |
169 | 8. Limitation of Liability. In no event and under no legal theory,
170 | whether in tort (including negligence), contract, or otherwise,
171 | unless required by applicable law (such as deliberate and grossly
172 | negligent acts) or agreed to in writing, shall any Contributor be
173 | liable to You for damages, including any direct, indirect, special,
174 | incidental, or consequential damages of any character arising as a
175 | result of this License or out of the use or inability to use the
176 | Work (including but not limited to damages for loss of goodwill,
177 | work stoppage, computer failure or malfunction, or any and all
178 | other commercial damages or losses), even if such Contributor
179 | has been advised of the possibility of such damages.
180 |
181 | 9. Accepting Warranty or Additional Liability. While redistributing
182 | the Work or Derivative Works thereof, You may choose to offer,
183 | and charge a fee for, acceptance of support, warranty, indemnity,
184 | or other liability obligations and/or rights consistent with this
185 | License. However, in accepting such obligations, You may act only
186 | on Your own behalf and on Your sole responsibility, not on behalf
187 | of any other Contributor, and only if You agree to indemnify,
188 | defend, and hold each Contributor harmless for any liability
189 | incurred by, or claims asserted against, such Contributor by reason
190 | of your accepting any such warranty or additional liability.
191 |
192 | END OF TERMS AND CONDITIONS
193 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Riak Elixir Client
2 | [](https://travis-ci.org/drewkerrigan/riak-elixir-client)
3 | [](https://hex.pm/packages/riak)
4 | 
5 | [](https://waffle.io/drewkerrigan/riak-elixir-client)
6 |
7 | A Riak client written in Elixir. Now includes connection pooling with [pooler](http://github.com/seth/pooler) and a variety of other improvements from [riex](https://github.com/edgurgel/riex).
8 |
9 | ## Setup
10 |
11 | ### Prerequisites
12 |
13 | * Riak 2.0+
14 | * Elixir 1.0+
15 |
16 | #### In an Elixir application
17 |
18 | Add the following to mix.exs
19 |
20 | ```elixir
21 | ...
22 | def application do
23 | [ applications: [ :riak ]]
24 | end
25 | ...
26 | defp deps do
27 | [ {:riak, "~> 1.1.6"} ]
28 | end
29 | ...
30 | ```
31 |
32 | ## Usage
33 |
34 | ### Establishing a Riak connection
35 |
36 | ```elixir
37 | {:ok, pid} = Riak.Connection.start_link('127.0.0.1', 8087) # Default values
38 | ```
39 |
40 | ### Connection Pooling
41 |
42 | 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`:
43 |
44 | ```elixir
45 | config :pooler, pools:
46 | [
47 | [
48 | name: :riaklocal1,
49 | group: :riak,
50 | max_count: 10,
51 | init_count: 5,
52 | start_mfa: { Riak.Connection, :start_link, [] }
53 | ], [
54 | name: :riaklocal2,
55 | group: :riak,
56 | max_count: 15,
57 | init_count: 2,
58 | start_mfa: { Riak.Connection, :start_link, ['127.0.0.1', 9090] }
59 | ]
60 | ]
61 | ```
62 |
63 | For an example using this functionality with a local Riak instance, check [`config/config.exs`](https://github.com/drewkerrigan/riak-elixir-client/blob/master/config/config.exs). More information about Elixir configuration can be found on [http://elixir-lang.org](http://elixir-lang.org): [Application environment and configuration](http://elixir-lang.org/getting-started/mix-otp/distributed-tasks-and-configuration.html#application-environment-and-configuration).
64 |
65 | Once a pool configuration is properly defined in a project, calls to Riak can omit the pid. For example:
66 |
67 | This call uses a pid from the pool of connections provided by pooler:
68 |
69 | ```elixir
70 | Riak.delete("user", key)
71 | ```
72 |
73 | This call requires a pid obtained by first calling `Riak.Connection.start_link`:
74 |
75 | ```elixir
76 | Riak.delete(pid, "user", key)
77 | ```
78 |
79 | ### Save a value
80 |
81 | ```elixir
82 | o = Riak.Object.create(bucket: "user", key: "my_key", data: "Han Solo")
83 | Riak.put(pid, o)
84 | ```
85 |
86 | ### Find an object
87 |
88 | ```elixir
89 | o = Riak.find(pid, "user", "my_key")
90 | ```
91 |
92 | ### Update an object
93 |
94 | ```elixir
95 | o = %{o | data: "Something Else"}
96 | Riak.put(pid, o)
97 | ```
98 |
99 | ### Delete an object
100 |
101 | Using key
102 |
103 | ```elixir
104 | Riak.delete(pid, "user", key)
105 | ```
106 |
107 | Using object
108 |
109 | ```elixir
110 | Riak.delete(pid, o)
111 | ```
112 |
113 | ### Timeseries
114 |
115 | Riak Timeseries functionality is available in [TS 1.3.1 releases of Riak](http://docs.basho.com/riak/ts/1.3.1/downloads/) and greater.
116 |
117 | #### Setup
118 |
119 | Create a table:
120 |
121 | ```
122 | 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))"}}'
123 | riak-admin bucket-type activate GeoCheckin
124 | ```
125 |
126 | #### Insert Rows
127 |
128 | ```
129 | Riak.Timeseries.put("GeoCheckin", [
130 | {"region1", "state1", 25, "hot", 23.0},
131 | {"region2", "state99", 26, "windy", 19.0}
132 | ])
133 | > :ok
134 | ```
135 |
136 | #### Get a row by primary key
137 |
138 | ```
139 | Riak.Timeseries.get("GeoCheckin", ["region1", "state1", 25])
140 | > {["region", "state", "time", "weather", "temperature"], [{"region1", "state1", 25, "hot", 23.0}]}
141 | ```
142 |
143 | #### Get all rows
144 |
145 | *Note*: This is a very expensive operation for a loaded cluster
146 |
147 | ```
148 | Riak.Timeseries.list!("GeoCheckin")
149 | > [{"region1", "state1", 25, "hot", 23.0}, {"region2", "state99", 26, "windy", 19.0}]
150 | ```
151 |
152 | #### Delete a row
153 |
154 | ```
155 | Riak.Timeseries.delete("GeoCheckin", ["region2", "state99", 26])
156 | > :ok
157 | ```
158 |
159 | #### Query
160 |
161 | ```
162 | Riak.Timeseries.query("select * from GeoCheckin where time > 24 and time < 26 and region = 'region1' and state = 'state1'")
163 | > {["region", "state", "time", "weather", "temperature"], [{"region1", "state1", 25, "hot", 23.0}]}
164 | ```
165 |
166 | ### Datatypes
167 |
168 | Riak Datatypes (a.k.a. CRDTs) are avaiable in [Riak versions 2.0](http://basho.com/introducing-riak-2-0/) and greater. The types included are: maps, sets, counters, registers and flags.
169 |
170 | #### Setup
171 |
172 | 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.
173 |
174 | 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:
175 |
176 | Bucket-Type: `counters`
177 |
178 | ```
179 | riak-admin bucket-type create counters '{"props":{"datatype":"counter"}}'
180 | riak-admin bucket-type activate counters
181 | ```
182 |
183 | Bucket-Type: `sets`
184 |
185 | ```
186 | riak-admin bucket-type create sets '{"props":{"datatype":"set"}}'
187 | riak-admin bucket-type activate sets
188 | ```
189 |
190 | Bucket-Type: `maps`
191 |
192 | ```
193 | riak-admin bucket-type create maps '{"props":{"datatype":"map"}}'
194 | riak-admin bucket-type activate maps
195 | ```
196 |
197 | Bucket-Type: `hll`
198 |
199 | ```
200 | riak-admin bucket-type create hll '{"props":{"datatype":"hll"}}'
201 | riak-admin bucket-type activate hll
202 | ```
203 |
204 | #### Counters
205 |
206 | Create a counter (`alias Riak.CRDT.Counter`):
207 |
208 | ```elixir
209 | Counter.new
210 | |> Counter.increment
211 | |> Counter.increment(2)
212 | |> Riak.update("counters", "my_counter_bucket", "my_key")
213 | ```
214 |
215 | Fetch a counter:
216 |
217 | ```elixir
218 | counter = Riak.find("counters", "my_counter_bucket", "my_key")
219 | |> Counter.value
220 | ```
221 |
222 | `counter` will be 3.
223 |
224 | ***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.
225 |
226 | 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](https://github.com/drewkerrigan/riak-ruby-ledger). Another approach could be the client-side implementation of a HAT (Highly Available Transaction) algorithm.
227 |
228 | #### Sets
229 |
230 | Create a set (`alias Riak.CRDT.Set`):
231 |
232 | ```elixir
233 | Set.new
234 | |> Set.put("foo")
235 | |> Set.put("bar")
236 | |> Riak.update("sets", "my_set_bucket", "my_key")
237 | ```
238 |
239 | And fetch the set:
240 |
241 | ```elixir
242 | set = Riak.find("sets", "my_set_bucket", "my_key")
243 | |> Set.value
244 | ```
245 |
246 | Where `set` is an `orddict`.
247 |
248 | #### Maps
249 |
250 | Maps handle binary keys with any other datatype (map, set, flag, register and counter).
251 |
252 | Create a map (`alias Riak.CRDT.Map`):
253 |
254 | ```elixir
255 | register = Register.new("some string")
256 | flag = Flag.new |> Flag.enable
257 | Map.new
258 | |> Map.put("k1", register)
259 | |> Map.put("k2", flag)
260 | |> Riak.update("maps", "my_map_bucket", "map_key")
261 | ```
262 |
263 | And fetch the map:
264 |
265 | ```elixir
266 | map = Riak.find("maps", "my_map_bucket", key) |> Map.value
267 | ```
268 |
269 | Where `map` is an `orddict`.
270 |
271 | #### Hyper Log Logs
272 |
273 | The use case for this type is counting distinct elements in a monotonic way.
274 | I think of it as like a counter for customers visited but once a customer visits
275 | the counter will never go up again. It also isn't possible to remove a element
276 | once it has been added to the log.
277 |
278 | Create a HLL (`alias Riak.CRDT.HyperLogLog`):
279 |
280 | ```elixir
281 | HyperLogLog.new
282 | |> HyperLogLog.add_element("foo")
283 | |> Riak.update("hll", "my_hll_bucket", "hll_key")
284 | ```
285 |
286 | And fetch the distinct count:
287 |
288 | ```elixir
289 | hll = Riak.find("hll", "my_hll_bucket", "hll_key") |> HLL.value
290 | ```
291 |
292 | Where `hll` is an `integer`.
293 |
294 | ## Examples
295 |
296 | Check the `examples/` directory for a few example elixir applications using the riak client.
297 |
298 | For more functionality, check `test/` directory.
299 |
300 | ## Tests
301 |
302 | ```
303 | MIX_ENV=test mix do deps.get, test
304 | ```
305 |
306 | ***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.
307 |
308 |
309 | *Note*
310 |
311 | The creation of the following CRDT bucket-types is a prerequisite for passing the CRDT tests.
312 |
313 | ```
314 | riak-admin bucket-type create maps '{"props":{"datatype":"map"}}'
315 | riak-admin bucket-type activate maps
316 | riak-admin bucket-type create sets '{"props":{"datatype":"set"}}'
317 | riak-admin bucket-type activate sets
318 | riak-admin bucket-type create counters '{"props":{"datatype":"counter"}}'
319 | riak-admin bucket-type activate counters
320 | riak-admin bucket-type create hll '{"props":{"datatype":"hll"}}'
321 | riak-admin bucket-type activate hll
322 | ```
323 |
324 | *Note*
325 |
326 | The creation of this Timeseries table is a prerequisite for passing the Timeseries tests.
327 |
328 | ```
329 | 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))"}}'
330 | riak-admin bucket-type activate GeoCheckin
331 | ```
332 |
333 |
334 | ## License
335 |
336 | Copyright 2017 Drew Kerrigan.
337 | Copyright 2014 Eduardo Gurgel.
338 |
339 | Licensed under the Apache License, Version 2.0 (the "License");
340 | you may not use this file except in compliance with the License.
341 | You may obtain a copy of the License at
342 |
343 | http://www.apache.org/licenses/LICENSE-2.0
344 |
345 | Unless required by applicable law or agreed to in writing, software
346 | distributed under the License is distributed on an "AS IS" BASIS,
347 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
348 | See the License for the specific language governing permissions and
349 | limitations under the License.
350 |
--------------------------------------------------------------------------------
/THANKS:
--------------------------------------------------------------------------------
1 | The following people have contributed to the Riak Elixir client:
2 |
3 | THANKS
4 | ------
5 |
6 | Drew Kerrigan
7 | Eduardo Gurgel
8 | Eric Liaw
9 | Justin McNally
10 | Lucas Allan
11 | Martin Gausby
12 | Paulo Almeida
--------------------------------------------------------------------------------
/config/config.exs:
--------------------------------------------------------------------------------
1 | use Mix.Config
2 |
3 | config :pooler, pools:
4 | [
5 | [
6 | name: :riaklocal,
7 | group: :riak,
8 | max_count: 10,
9 | init_count: 5,
10 | start_mfa: { Riak.Connection, :start_link, [] }
11 | ]
12 | ]
13 |
--------------------------------------------------------------------------------
/doc/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 404 – Riak Elixir Client v1.1.6
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
Page not found
66 |
67 |
Sorry, but the page you were trying to get to, does not exist. You
68 | may want to try searching this site using the sidebar or using our
69 | API Reference page to find what
70 | you were looking for.
71 |
72 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/doc/Riak.CRDT.Counter.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Riak.CRDT.Counter – Riak Elixir Client v1.1.6
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Riak Elixir Client v1.1.6
67 | Riak.CRDT.Counter
68 |
69 |
70 |
71 | View Source
72 |
73 |
74 |
75 |
76 |
77 |
78 | Counter data-type on Riak 2.0.
79 |
80 |
81 |
82 |
83 |
84 |
85 |
92 |
93 |
94 |
95 |
96 |
99 |
100 |
103 |
104 |
Decrement a counter
on the amount
defaulting in 1
105 |
106 |
107 |
108 |
109 |
112 |
113 |
Increment a counter
on the amount
defaulting in 1
114 |
115 |
116 |
117 |
118 |
121 |
122 |
Create a new counter
123 |
124 |
125 |
126 |
127 |
130 |
131 |
Get the original value as a number
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
155 |
156 |
157 |
158 |
159 |
174 |
175 | Decrement a counter
on the amount
defaulting in 1
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
198 |
199 | Increment a counter
on the amount
defaulting in 1
200 |
201 |
202 |
203 |
204 |
205 |
220 |
221 | Create a new counter
222 |
223 |
224 |
225 |
226 |
227 |
242 |
243 | Get the original value as a number
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
--------------------------------------------------------------------------------
/doc/Riak.CRDT.Flag.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Riak.CRDT.Flag – Riak Elixir Client v1.1.6
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Riak Elixir Client v1.1.6
67 | Riak.CRDT.Flag
68 |
69 |
70 |
71 | View Source
72 |
73 |
74 |
75 |
76 |
77 |
78 | Encapsulates a boolean datatype inside a CRDT.Map
79 |
80 |
81 |
82 |
83 |
84 |
85 |
92 |
93 |
94 |
95 |
96 |
99 |
100 |
103 |
104 |
Turns the value to false
105 |
106 |
107 |
108 |
109 |
112 |
113 |
Turns the value to true
114 |
115 |
116 |
117 |
118 |
121 |
122 |
Creates a new flag container
123 |
124 |
125 |
126 |
132 |
138 |
139 |
142 |
143 |
Extracts current value of flag
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
167 |
168 |
169 |
184 |
185 | Turns the value to false
186 |
187 |
188 |
189 |
190 |
191 |
206 |
207 | Turns the value to true
208 |
209 |
210 |
211 |
212 |
213 |
228 |
229 | Creates a new flag container
230 |
231 |
232 |
233 |
234 |
235 |
250 |
253 |
254 |
255 |
256 |
271 |
274 |
275 |
276 |
277 |
292 |
293 | Extracts current value of flag
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
--------------------------------------------------------------------------------
/doc/Riak.CRDT.HyperLogLog.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Riak.CRDT.HyperLogLog – Riak Elixir Client v1.1.6
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Riak Elixir Client v1.1.6
67 | Riak.CRDT.HyperLogLog
68 |
69 |
70 |
71 | View Source
72 |
73 |
74 |
75 |
76 |
77 |
78 | Encapsulates Riak HLL
79 |
80 |
81 |
82 |
83 |
84 |
85 |
92 |
93 |
94 |
95 |
96 |
99 |
100 |
103 |
104 |
Adds a distinct element to the HLL
105 |
106 |
107 |
108 |
109 |
112 |
113 |
Adds a list of elements to the HLL
114 |
115 |
116 |
117 |
118 |
121 |
122 |
124 |
125 |
126 |
127 |
130 |
131 |
Fetch the value of the HLL
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
155 |
156 |
157 |
172 |
173 | Adds a distinct element to the HLL
174 |
175 |
176 |
177 |
178 |
179 |
194 |
195 | Adds a list of elements to the HLL
196 |
197 |
198 |
199 |
200 |
201 |
216 |
217 | Creates a new hll
218 |
219 |
220 |
221 |
222 |
223 |
238 |
239 | Fetch the value of the HLL
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
--------------------------------------------------------------------------------
/doc/Riak.CRDT.Register.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Riak.CRDT.Register – Riak Elixir Client v1.1.6
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Riak Elixir Client v1.1.6
67 | Riak.CRDT.Register
68 |
69 |
70 |
71 | View Source
72 |
73 |
74 |
75 |
76 |
77 |
78 | Encapsulates a binary data to be used on CRDT.Map’s
79 |
80 |
81 |
82 |
83 |
84 |
85 |
92 |
93 |
94 |
95 |
96 |
99 |
100 |
103 |
104 |
Creates a new register
105 |
106 |
107 |
108 |
109 |
112 |
113 |
Creates a new register with the initial value
114 |
115 |
116 |
117 |
118 |
121 |
122 |
Set the value
on the register
123 |
124 |
125 |
126 |
127 |
130 |
131 |
Extracts current value of register
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
155 |
156 |
157 |
172 |
173 | Creates a new register
174 |
175 |
176 |
177 |
178 |
179 |
194 |
195 | Creates a new register with the initial value
196 |
197 |
198 |
199 |
200 |
201 |
216 |
217 | Set the value
on the register
218 |
219 |
220 |
221 |
222 |
223 |
238 |
239 | Extracts current value of register
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
--------------------------------------------------------------------------------
/doc/Riak.CRDT.Set.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Riak.CRDT.Set – Riak Elixir Client v1.1.6
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Riak Elixir Client v1.1.6
67 | Riak.CRDT.Set
68 |
69 |
70 |
71 | View Source
72 |
73 |
74 |
75 |
76 |
77 |
78 | Encapsulates riakc_set using elixir Set functions as interface.
79 | It accepts only binary as items.
80 |
81 |
82 |
83 |
84 |
85 |
86 |
93 |
94 |
95 |
96 |
97 |
100 |
101 |
104 |
105 |
Delete value
on set
106 |
107 |
108 |
109 |
110 |
113 |
114 |
Checks if set
contains value
115 |
116 |
117 |
118 |
119 |
122 |
123 |
Create an empty set
124 |
125 |
126 |
127 |
133 |
139 |
140 |
143 |
144 |
Insert value
on set
145 |
146 |
147 |
148 |
149 |
152 |
153 |
Returns the number of elements in set
154 |
155 |
156 |
157 |
158 |
161 |
162 |
Get original value as an ordset
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
186 |
187 |
188 |
203 |
204 | Delete value
on set
205 |
206 |
207 |
208 |
209 |
210 |
225 |
226 | Checks if set
contains value
.
227 |
228 |
229 |
230 |
231 |
232 |
247 |
248 | Create an empty set
249 |
250 |
251 |
252 |
253 |
254 |
269 |
272 |
273 |
274 |
275 |
290 |
293 |
294 |
295 |
296 |
311 |
312 | Insert value
on set
313 |
314 |
315 |
316 |
317 |
318 |
333 |
334 | Returns the number of elements in set
335 |
336 |
337 |
338 |
339 |
340 |
355 |
356 | Get original value as an ordset
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
--------------------------------------------------------------------------------
/doc/Riak.CRDT.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Riak.CRDT – Riak Elixir Client v1.1.6
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Riak Elixir Client v1.1.6
67 | Riak.CRDT
68 |
69 |
70 |
71 | View Source
72 |
73 |
74 |
75 |
76 |
77 |
78 | Common CRDT module
79 |
80 |
81 |
82 |
83 |
84 |
85 |
92 |
93 |
94 |
95 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
125 |
126 |
127 |
142 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
--------------------------------------------------------------------------------
/doc/Riak.Connection.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Riak.Connection – Riak Elixir Client v1.1.6
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Riak Elixir Client v1.1.6
67 | Riak.Connection
68 |
69 |
70 |
71 | View Source
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
87 |
88 |
89 |
90 |
91 |
94 |
95 |
98 |
99 |
Create a process to talk with the riak server on host:port
100 |
101 |
102 |
103 |
104 |
107 |
108 |
Create a linked process to talk with the riak server on host:port
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
155 |
156 | Create a process to talk with the riak server on host:port.
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
183 |
184 | Create a linked process to talk with the riak server on host:port.
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
--------------------------------------------------------------------------------
/doc/Riak.Index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Riak.Index – Riak Elixir Client v1.1.6
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Riak Elixir Client v1.1.6
67 | Riak.Index
68 |
69 |
70 |
71 | View Source
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
87 |
88 |
89 |
90 |
91 |
94 |
100 |
101 |
104 |
105 |
@deprecated There are performance issues with 2i, please use another query strategy.
106 | Execute a secondary index equality query
107 |
108 |
109 |
110 |
111 |
114 |
115 |
@deprecated There are performance issues with 2i, please use another query strategy.
116 | Execute a secondary index range query
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
140 |
141 |
142 |
157 |
160 |
161 |
162 |
163 |
178 |
179 | @deprecated There are performance issues with 2i, please use another query strategy.
180 | Execute a secondary index equality query.
181 |
182 |
183 |
184 |
185 |
186 |
201 |
202 | @deprecated There are performance issues with 2i, please use another query strategy.
203 | Execute a secondary index range query.
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
--------------------------------------------------------------------------------
/doc/Riak.Mapred.Bucket.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Riak.Mapred.Bucket – Riak Elixir Client v1.1.6
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Riak Elixir Client v1.1.6
67 | Riak.Mapred.Bucket
68 |
69 |
70 |
71 | View Source
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
87 |
88 |
89 |
90 |
91 |
94 |
100 |
101 |
104 |
105 |
@deprecated There are performance issues with MapReduce, please use another query strategy.
106 | Perform a MapReduce job across the cluster using an entire bucket as the input.
107 | See the MapReduce documentation for explanation of behavior
108 |
109 |
110 |
111 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
137 |
138 |
139 |
154 |
157 |
158 |
159 |
160 |
175 |
176 | @deprecated There are performance issues with MapReduce, please use another query strategy.
177 | Perform a MapReduce job across the cluster using an entire bucket as the input.
178 | See the MapReduce documentation for explanation of behavior.
179 |
180 |
181 |
182 |
183 |
184 |
199 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
--------------------------------------------------------------------------------
/doc/Riak.Mapred.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Riak.Mapred – Riak Elixir Client v1.1.6
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Riak Elixir Client v1.1.6
67 | Riak.Mapred
68 |
69 |
70 |
71 | View Source
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
87 |
88 |
89 |
90 |
91 |
94 |
100 |
101 |
104 |
105 |
@deprecated There are performance issues with MapReduce, please use another query strategy.
106 | Perform a MapReduce job across the cluster.
107 | See the MapReduce documentation for explanation of behavior
108 |
109 |
110 |
111 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
137 |
138 |
139 |
154 |
157 |
158 |
159 |
160 |
175 |
176 | @deprecated There are performance issues with MapReduce, please use another query strategy.
177 | Perform a MapReduce job across the cluster.
178 | See the MapReduce documentation for explanation of behavior.
179 |
180 |
181 |
182 |
183 |
184 |
199 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
--------------------------------------------------------------------------------
/doc/Riak.Pool.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Riak.Pool – Riak Elixir Client v1.1.6
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Riak Elixir Client v1.1.6
67 | Riak.Pool
68 |
69 |
70 |
71 | View Source
72 |
73 |
74 |
75 |
76 |
77 |
78 | [EXPERIMENTAL]
79 | This module adds defpool to define functions with a
80 | lower arity for each function so:
81 | Riak.put(pid, bucket, key, data) ->
82 | Riak.put(bucket, key, data) that calls the previous function
83 | with a pid from the pool
84 |
85 |
86 |
87 |
88 |
89 |
90 |
97 |
98 |
99 |
100 |
101 |
104 |
110 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
136 |
137 |
138 |
155 |
158 |
159 |
160 |
161 |
162 |
163 |
178 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
--------------------------------------------------------------------------------
/doc/Riak.Search.Schema.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Riak.Search.Schema – Riak Elixir Client v1.1.6
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Riak Elixir Client v1.1.6
67 | Riak.Search.Schema
68 |
69 |
70 |
71 | View Source
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
87 |
88 |
89 |
90 |
91 |
94 |
100 |
106 |
112 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
138 |
139 |
140 |
155 |
158 |
159 |
160 |
161 |
176 |
179 |
180 |
181 |
182 |
197 |
200 |
201 |
202 |
203 |
218 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
--------------------------------------------------------------------------------
/doc/Riak.Search.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Riak.Search – Riak Elixir Client v1.1.6
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Riak Elixir Client v1.1.6
67 | Riak.Search
68 |
69 |
70 |
71 | View Source
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
87 |
88 |
89 |
90 |
91 |
94 |
100 |
106 |
112 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
138 |
139 |
140 |
155 |
158 |
159 |
160 |
161 |
176 |
179 |
180 |
181 |
182 |
197 |
200 |
201 |
202 |
203 |
218 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
--------------------------------------------------------------------------------
/doc/api-reference.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | API Reference – Riak Elixir Client v1.1.6
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Riak Elixir Client v1.1.6
67 | API Reference
68 |
69 |
70 |
71 |
72 |
73 |
74 | Modules
75 |
76 |
77 |
78 |
79 |
80 |
81 |
Riak Elixir Client
82 |
83 |
84 |
85 |
86 |
90 |
94 |
95 |
96 |
97 |
99 |
100 |
101 |
102 |
103 |
104 |
Counter data-type on Riak 2.0
105 |
106 |
107 |
108 |
109 |
110 |
111 |
Encapsulates a boolean datatype inside a CRDT.Map
112 |
113 |
114 |
115 |
116 |
117 |
118 |
Encapsulates Riak HLL
119 |
120 |
121 |
122 |
123 |
124 |
125 |
Encapsulates Riak maps
126 |
127 |
128 |
129 |
130 |
131 |
132 |
Encapsulates a binary data to be used on CRDT.Map’s
133 |
134 |
135 |
136 |
137 |
138 |
139 |
Encapsulates riakc_set using elixir Set functions as interface.
140 | It accepts only binary as items
141 |
142 |
143 |
144 |
148 |
152 |
156 |
160 |
161 |
162 |
163 |
The Data wrapper makes it convenient to work with Riak data in Elixir
164 |
165 |
166 |
167 |
168 |
169 |
170 |
[EXPERIMENTAL]
171 | This module adds defpool to define functions with a
172 | lower arity for each function so
173 |
174 |
175 |
176 |
180 |
184 |
188 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
--------------------------------------------------------------------------------
/doc/dist/sidebar_items-8bfd31e79d.js:
--------------------------------------------------------------------------------
1 | sidebarNodes={"extras":[{"id":"api-reference","title":"API Reference","group":"","headers":[{"id":"Modules","anchor":"modules"}]}],"exceptions":[],"modules":[{"id":"Riak","title":"Riak","functions":[{"id":"delete/1","anchor":"delete/1"},{"id":"delete/2","anchor":"delete/2"},{"id":"delete/3","anchor":"delete/3"},{"id":"delete/4","anchor":"delete/4"},{"id":"find/2","anchor":"find/2"},{"id":"find/3","anchor":"find/3"},{"id":"find/4","anchor":"find/4"},{"id":"ping/0","anchor":"ping/0"},{"id":"ping/1","anchor":"ping/1"},{"id":"put/1","anchor":"put/1"},{"id":"put/2","anchor":"put/2"},{"id":"resolve/3","anchor":"resolve/3"},{"id":"resolve/4","anchor":"resolve/4"},{"id":"update/4","anchor":"update/4"},{"id":"update/5","anchor":"update/5"}]},{"id":"Riak.Bucket","title":"Riak.Bucket","functions":[{"id":"get/1","anchor":"get/1"},{"id":"get/2","anchor":"get/2"},{"id":"keys/1","anchor":"keys/1"},{"id":"keys/2","anchor":"keys/2"},{"id":"keys/3","anchor":"keys/3"},{"id":"keys/4","anchor":"keys/4"},{"id":"keys!/1","anchor":"keys!/1"},{"id":"keys!/2","anchor":"keys!/2"},{"id":"keys!/3","anchor":"keys!/3"},{"id":"keys!/4","anchor":"keys!/4"},{"id":"list/0","anchor":"list/0"},{"id":"list/1","anchor":"list/1"},{"id":"list/2","anchor":"list/2"},{"id":"list!/0","anchor":"list!/0"},{"id":"list!/1","anchor":"list!/1"},{"id":"list!/2","anchor":"list!/2"},{"id":"possible_props/0","anchor":"possible_props/0"},{"id":"put/2","anchor":"put/2"},{"id":"put/3","anchor":"put/3"},{"id":"put/4","anchor":"put/4"},{"id":"reset/1","anchor":"reset/1"},{"id":"reset/2","anchor":"reset/2"}]},{"id":"Riak.Bucket.Type","title":"Riak.Bucket.Type","functions":[{"id":"get/1","anchor":"get/1"},{"id":"get/2","anchor":"get/2"},{"id":"list/1","anchor":"list/1"},{"id":"list/2","anchor":"list/2"},{"id":"list/3","anchor":"list/3"},{"id":"list!/1","anchor":"list!/1"},{"id":"list!/2","anchor":"list!/2"},{"id":"list!/3","anchor":"list!/3"},{"id":"put/2","anchor":"put/2"},{"id":"put/3","anchor":"put/3"}]},{"id":"Riak.CRDT","title":"Riak.CRDT","functions":[{"id":"type/1","anchor":"type/1"}]},{"id":"Riak.CRDT.Counter","title":"Riak.CRDT.Counter","functions":[{"id":"decrement/2","anchor":"decrement/2"},{"id":"increment/2","anchor":"increment/2"},{"id":"new/0","anchor":"new/0"},{"id":"value/1","anchor":"value/1"}]},{"id":"Riak.CRDT.Flag","title":"Riak.CRDT.Flag","functions":[{"id":"disable/1","anchor":"disable/1"},{"id":"enable/1","anchor":"enable/1"},{"id":"new/0","anchor":"new/0"},{"id":"new/1","anchor":"new/1"},{"id":"new/2","anchor":"new/2"},{"id":"value/1","anchor":"value/1"}]},{"id":"Riak.CRDT.HyperLogLog","title":"Riak.CRDT.HyperLogLog","functions":[{"id":"add_element/2","anchor":"add_element/2"},{"id":"add_elements/2","anchor":"add_elements/2"},{"id":"new/0","anchor":"new/0"},{"id":"value/1","anchor":"value/1"}]},{"id":"Riak.CRDT.Map","title":"Riak.CRDT.Map","functions":[{"id":"delete/2","anchor":"delete/2"},{"id":"get/3","anchor":"get/3"},{"id":"has_key?/2","anchor":"has_key?/2"},{"id":"keys/1","anchor":"keys/1"},{"id":"new/0","anchor":"new/0"},{"id":"put/3","anchor":"put/3"},{"id":"size/1","anchor":"size/1"},{"id":"update/4","anchor":"update/4"},{"id":"value/1","anchor":"value/1"}]},{"id":"Riak.CRDT.Register","title":"Riak.CRDT.Register","functions":[{"id":"new/0","anchor":"new/0"},{"id":"new/1","anchor":"new/1"},{"id":"set/2","anchor":"set/2"},{"id":"value/1","anchor":"value/1"}]},{"id":"Riak.CRDT.Set","title":"Riak.CRDT.Set","functions":[{"id":"delete/2","anchor":"delete/2"},{"id":"member?/2","anchor":"member?/2"},{"id":"new/0","anchor":"new/0"},{"id":"new/1","anchor":"new/1"},{"id":"new/2","anchor":"new/2"},{"id":"put/2","anchor":"put/2"},{"id":"size/1","anchor":"size/1"},{"id":"value/1","anchor":"value/1"}]},{"id":"Riak.Connection","title":"Riak.Connection","functions":[{"id":"start/3","anchor":"start/3"},{"id":"start_link/3","anchor":"start_link/3"}]},{"id":"Riak.Index","title":"Riak.Index","functions":[{"id":"query/4","anchor":"query/4"},{"id":"query/5","anchor":"query/5"},{"id":"query/6","anchor":"query/6"}]},{"id":"Riak.Mapred","title":"Riak.Mapred","functions":[{"id":"query/2","anchor":"query/2"},{"id":"query/3","anchor":"query/3"},{"id":"query/4","anchor":"query/4"}]},{"id":"Riak.Mapred.Bucket","title":"Riak.Mapred.Bucket","functions":[{"id":"query/2","anchor":"query/2"},{"id":"query/3","anchor":"query/3"},{"id":"query/4","anchor":"query/4"}]},{"id":"Riak.Object","title":"Riak.Object","functions":[{"id":"__struct__/0","anchor":"__struct__/0"},{"id":"create/1","anchor":"create/1"},{"id":"delete_all_indexes/1","anchor":"delete_all_indexes/1"},{"id":"delete_all_links/1","anchor":"delete_all_links/1"},{"id":"delete_all_metadata/1","anchor":"delete_all_metadata/1"},{"id":"delete_index/2","anchor":"delete_index/2"},{"id":"delete_link/2","anchor":"delete_link/2"},{"id":"delete_metadata/2","anchor":"delete_metadata/2"},{"id":"from_robj/1","anchor":"from_robj/1"},{"id":"get_all_indexes/1","anchor":"get_all_indexes/1"},{"id":"get_all_links/1","anchor":"get_all_links/1"},{"id":"get_all_metadata/1","anchor":"get_all_metadata/1"},{"id":"get_index/2","anchor":"get_index/2"},{"id":"get_link/2","anchor":"get_link/2"},{"id":"get_metadata/2","anchor":"get_metadata/2"},{"id":"index_id/1","anchor":"index_id/1"},{"id":"put_index/3","anchor":"put_index/3"},{"id":"put_link/4","anchor":"put_link/4"},{"id":"put_metadata/2","anchor":"put_metadata/2"},{"id":"to_robj/1","anchor":"to_robj/1"}]},{"id":"Riak.Pool","title":"Riak.Pool","functions":[{"id":"defpool/2","anchor":"defpool/2"},{"id":"take_group_member/2","anchor":"take_group_member/2"}]},{"id":"Riak.Search","title":"Riak.Search","functions":[{"id":"query/2","anchor":"query/2"},{"id":"query/3","anchor":"query/3"},{"id":"query/4","anchor":"query/4"},{"id":"query/5","anchor":"query/5"}]},{"id":"Riak.Search.Index","title":"Riak.Search.Index","functions":[{"id":"delete/1","anchor":"delete/1"},{"id":"delete/2","anchor":"delete/2"},{"id":"get/1","anchor":"get/1"},{"id":"get/2","anchor":"get/2"},{"id":"list/0","anchor":"list/0"},{"id":"list/1","anchor":"list/1"},{"id":"put/1","anchor":"put/1"},{"id":"put/2","anchor":"put/2"},{"id":"put/3","anchor":"put/3"},{"id":"put/4","anchor":"put/4"}]},{"id":"Riak.Search.Schema","title":"Riak.Search.Schema","functions":[{"id":"create/2","anchor":"create/2"},{"id":"create/3","anchor":"create/3"},{"id":"get/1","anchor":"get/1"},{"id":"get/2","anchor":"get/2"}]},{"id":"Riak.Timeseries","title":"Riak.Timeseries","functions":[{"id":"delete/2","anchor":"delete/2"},{"id":"delete/3","anchor":"delete/3"},{"id":"delete/4","anchor":"delete/4"},{"id":"get/2","anchor":"get/2"},{"id":"get/3","anchor":"get/3"},{"id":"get/4","anchor":"get/4"},{"id":"list!/1","anchor":"list!/1"},{"id":"list!/2","anchor":"list!/2"},{"id":"list!/3","anchor":"list!/3"},{"id":"put/2","anchor":"put/2"},{"id":"put/3","anchor":"put/3"},{"id":"query/1","anchor":"query/1"},{"id":"query/2","anchor":"query/2"}]}],"protocols":[],"tasks":[]}
--------------------------------------------------------------------------------
/doc/fonts/icomoon.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenRiak/riak-elixir-client/c58700ec30a451a46b226a09387144becc47d5ae/doc/fonts/icomoon.eot
--------------------------------------------------------------------------------
/doc/fonts/icomoon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Generated by IcoMoon
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/doc/fonts/icomoon.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenRiak/riak-elixir-client/c58700ec30a451a46b226a09387144becc47d5ae/doc/fonts/icomoon.ttf
--------------------------------------------------------------------------------
/doc/fonts/icomoon.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenRiak/riak-elixir-client/c58700ec30a451a46b226a09387144becc47d5ae/doc/fonts/icomoon.woff
--------------------------------------------------------------------------------
/doc/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Riak Elixir Client v1.1.6 – Documentation
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/exploriak/.gitignore:
--------------------------------------------------------------------------------
1 | /_build
2 | /deps
3 | erl_crash.dump
4 | *.ez
5 |
--------------------------------------------------------------------------------
/examples/exploriak/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: all
2 |
3 | all:
4 | rm -f exploriak
5 | mix deps.get
6 | mix escript.build
--------------------------------------------------------------------------------
/examples/exploriak/README.md:
--------------------------------------------------------------------------------
1 | Exploriak
2 | ============
3 |
4 | A simple Elixir CLI app to perform a few actions against a local Riak node.
5 |
6 | ## Build
7 |
8 | ```
9 | make
10 | ```
11 |
12 | ## Usage
13 |
14 | Output of `./exploriak --help`:
15 |
16 | ```
17 | Usage:
18 | ./exploriak [operation] [options]
19 | Options:
20 | --help # Show this help message and quit.
21 | Listing:
22 | --list # (boolean) List keys or buckets
23 | --keys # (boolean) If not used, only buckets will be listed
24 | Selectors:
25 | --type # (string) Bucket type
26 | --bucket # (string) Bucket
27 |
28 | Examples:
29 | ./exploriak --list # List all buckets in default bucket type
30 | ./exploriak --list --type mytype # List all buckets in mytype bucket type
31 | ./exploriak --list --keys --bucket mybucket # List all keys in default bucket type, mybucket
32 | ```
--------------------------------------------------------------------------------
/examples/exploriak/exploriak:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OpenRiak/riak-elixir-client/c58700ec30a451a46b226a09387144becc47d5ae/examples/exploriak/exploriak
--------------------------------------------------------------------------------
/examples/exploriak/lib/exploriak.ex:
--------------------------------------------------------------------------------
1 | defmodule Exploriak do
2 |
3 | def main(args) do
4 | args |> parse_args |> process
5 | end
6 |
7 | def parse_args(args) do
8 | switches =
9 | [
10 | help: :boolean,
11 | ## List
12 | list: :boolean,
13 | keys: :boolean,
14 | type: :string,
15 | bucket: :string
16 | ]
17 |
18 | options = OptionParser.parse(args, switches: switches)
19 |
20 | case options do
21 | {[help: true],_,_} -> :help
22 | ## List
23 | {[list: true],_,_} -> [:list_buckets]
24 | {[list: true, type: type],_,_} -> [:list_buckets, type]
25 | {[list: true, keys: true],_,_} -> [:list_keys]
26 | {[list: true, keys: true, bucket: bucket],_,_} -> [:list_keys, bucket]
27 | {[list: true, keys: true, type: type, bucket: bucket],_,_} -> [:list_keys, type, bucket]
28 | _ -> :help
29 | end
30 | end
31 |
32 | def process(:help) do
33 | IO.puts """
34 | Usage:
35 | ./exploriak [operation] [options]
36 | Options:
37 | --help # Show this help message and quit.
38 | Listing:
39 | --list # (boolean) List keys or buckets
40 | --keys # (boolean) If not used, only buckets will be listed
41 | Selectors:
42 | --type # (string) Bucket type
43 | --bucket # (string) Bucket
44 |
45 | Examples:
46 | ./exploriak --list # List all buckets in default bucket type
47 | ./exploriak --list --type mytype # List all buckets in mytype bucket type
48 | ./exploriak --list --keys --bucket mybucket # List all keys in default bucket type, mybucket
49 |
50 | """
51 | System.halt(0)
52 | end
53 |
54 | ## List
55 | def process([:list_buckets]) do
56 | IO.puts "Listing all buckets in default type"
57 | for bucket <- Riak.Bucket.list!(get_pid) do
58 | IO.puts ["Bucket: ", bucket]
59 | end
60 | end
61 |
62 | def process([:list_buckets, type]) do
63 | IO.puts "Listing all buckets in default type"
64 | for bucket <- Riak.Bucket.Type.list!(get_pid, type) do
65 | IO.puts ["Bucket: ", bucket]
66 | end
67 | end
68 |
69 | def process([:list_keys, bucket]) do
70 | IO.puts ["Listing all keys in default type, ", bucket, " bucket"]
71 | for key <- Riak.Bucket.keys!(get_pid, bucket) do
72 | IO.puts ["Bucket: ", bucket, ", Key: ", key]
73 | end
74 | end
75 |
76 | def process([:list_keys, type, bucket]) do
77 | IO.puts ["Listing all keys in ", type, " type, ", bucket, " bucket"]
78 | for key <- Riak.Bucket.Type.keys!(get_pid, type, bucket) do
79 | IO.puts ["Bucket: ", bucket, ", Key: ", key]
80 | end
81 | end
82 |
83 | def process([:list_keys]) do
84 | IO.puts ["Listing all keys in default type, all buckets"]
85 | for bucket <- Riak.Bucket.list!(get_pid), key <- Riak.Bucket.keys!(get_pid, bucket) do
86 | IO.puts ["Bucket: ", bucket, ", Key: ", key]
87 | end
88 | end
89 |
90 | defp get_pid do
91 | {:ok, pid } = Riak.Connection.start('127.0.0.1', 8087)
92 | pid
93 | end
94 | end
95 |
--------------------------------------------------------------------------------
/examples/exploriak/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule Exploriak.Mixfile do
2 | use Mix.Project
3 |
4 | def project do
5 | [ app: :exploriak,
6 | version: "0.0.1",
7 | elixir: "~> 1.0",
8 | deps: deps,
9 | escript: escript ]
10 | end
11 |
12 | def escript do
13 | [ main_module: Exploriak,
14 | app: :riak ]
15 | end
16 |
17 | defp deps do
18 | [ {:riak, "~> 1.0"} ]
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/examples/exploriak/mix.lock:
--------------------------------------------------------------------------------
1 | %{"hamcrest": {:hex, :hamcrest, "0.1.1"},
2 | "linguist": {:hex, :linguist, "0.1.5"},
3 | "meck": {:hex, :meck, "0.8.3"},
4 | "pooler": {:hex, :pooler, "1.5.0"},
5 | "protobuffs": {:hex, :protobuffs, "0.8.2"},
6 | "riak": {:hex, :riak, "1.0.0"},
7 | "riak_pb": {:hex, :riak_pb, "2.1.0"},
8 | "riakc": {:hex, :riakc, "2.1.1"}}
9 |
--------------------------------------------------------------------------------
/examples/exploriak/test/exploriak_test.exs:
--------------------------------------------------------------------------------
1 | defmodule ExploriakTest do
2 | use ExUnit.Case
3 |
4 | test "the truth" do
5 | assert 1 + 1 == 2
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/examples/exploriak/test/test_helper.exs:
--------------------------------------------------------------------------------
1 | ExUnit.start()
2 |
--------------------------------------------------------------------------------
/lib/riak.ex:
--------------------------------------------------------------------------------
1 | defmodule Riak do
2 | @moduledoc """
3 | # Riak Elixir Client
4 | [](https://travis-ci.org/drewkerrigan/riak-elixir-client)
5 |
6 | A Riak client written in Elixir. Now includes connection pooling with [pooler](http://github.com/seth/pooler) and a variety of other improvements from [riex](https://github.com/edgurgel/riex).
7 |
8 | ## Setup
9 |
10 | ### Prerequisites
11 |
12 | * Riak 2.0+
13 | * Elixir 1.0+
14 |
15 | #### In an Elixir application
16 |
17 | Add the following to mix.exs
18 |
19 | ```elixir
20 | ...
21 | def application do
22 | [ applications: [ :riak ]]
23 | end
24 | ...
25 | defp deps do
26 | [ {:riak, "~> 1.0"} ]
27 | end
28 | ...
29 | ```
30 |
31 | ## Usage
32 |
33 | ### Establishing a Riak connection
34 |
35 | ```elixir
36 | {:ok, pid} = Riak.Connection.start_link('127.0.0.1', 8087) # Default values
37 | ```
38 |
39 | ### Connection Pooling
40 |
41 | 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`:
42 |
43 | ```elixir
44 | [pooler: [pools: [
45 | [ name: :riaklocal1,
46 | group: :riak,
47 | max_count: 10,
48 | init_count: 5,
49 | start_mfa: {Riak.Connection, :start_link, []}
50 | ],
51 | [ name: :riaklocal2,
52 | group: :riak,
53 | max_count: 15,
54 | init_count: 2,
55 | start_mfa: {Riak.Connection, :start_link, ['127.0.0.1', 9090]}
56 | ] ]
57 | ]]
58 |
59 | ```
60 |
61 | For an example using this functionality with a local Riak instance, check [`config/config.exs`](https://github.com/drewkerrigan/riak-elixir-client/blob/master/config/config.exs). More information about Elixir configuration can be found on [http://elixir-lang.org(http://elixir-lang.org)]: [Application environment and configuration](http://elixir-lang.org/getting_started/mix_otp/10.html#toc_6).
62 |
63 | Once a pool configuration is properly defined in a project, calls to Riak can omit the pid. For example:
64 |
65 | This call uses a pid from the pool of connections provided by pooler:
66 |
67 | ```elixir
68 | Riak.delete("user", key)
69 | ```
70 |
71 | This call requires a pid obtained by first calling `Riak.Connection.start_link`:
72 |
73 | ```elixir
74 | Riak.delete(pid, "user", key)
75 | ```
76 |
77 | ### Save a value
78 |
79 | ```elixir
80 | o = Riak.Object.create(bucket: "user", key: "my_key", data: "Han Solo")
81 | Riak.put(pid, o)
82 | ```
83 |
84 | ### Find an object
85 |
86 | ```elixir
87 | o = Riak.find(pid, "user", "my_key")
88 | ```
89 |
90 | ### Update an object
91 |
92 | ```elixir
93 | o = %{o | data: "Something Else"}
94 | Riak.put(pid, o)
95 | ```
96 |
97 | ### Delete an object
98 |
99 | Using key
100 |
101 | ```elixir
102 | Riak.delete(pid, "user", key)
103 | ```
104 |
105 | Using object
106 |
107 | ```elixir
108 | Riak.delete(pid, o)
109 | ```
110 |
111 | ### Datatypes
112 |
113 | Riak Datatypes (a.k.a. CRDTs) are avaiable in [Riak versions 2.0](http://basho.com/introducing-riak-2-0/) and greater. The types included are: maps, sets, counters, registers and flags.
114 |
115 | #### Setup
116 |
117 | Datatypes require the use of bucket-types. Maps, sets, and counters can be used as top-level bucket-type datatypes; Registers and flags may only be used within maps.
118 |
119 | The following examples assume the presence of 3 datatype enabled bucket-types. You can create these bucket-types by running the following commands on a single Riak node in your cluster:
120 |
121 | Bucket-Type: `counters`
122 |
123 | ```
124 | riak-admin bucket-type create counters '{"props":{"datatype":"counter"}}'
125 | riak-admin bucket-type activate counters
126 | ```
127 |
128 | Bucket-Type: `sets`
129 |
130 | ```
131 | riak-admin bucket-type create sets '{"props":{"datatype":"set"}}'
132 | riak-admin bucket-type activate sets
133 | ```
134 |
135 | Bucket-Type: `maps`
136 |
137 | ```
138 | riak-admin bucket-type create maps '{"props":{"datatype":"map"}}'
139 | riak-admin bucket-type activate maps
140 | ```
141 |
142 | #### Counters
143 |
144 | Create a counter (`alias Riak.CRDT.Counter`):
145 |
146 | ```elixir
147 | Counter.new
148 | |> Counter.increment
149 | |> Counter.increment(2)
150 | |> Riak.update("counters", "my_counter_bucket", "my_key")
151 | ```
152 |
153 | Fetch a counter:
154 |
155 | ```elixir
156 | counter = Riak.find("counters", "my_counter_bucket", "my_key")
157 | |> Counter.value
158 | ```
159 |
160 | `counter` will be 3.
161 |
162 | ***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.
163 |
164 | 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](https://github.com/drewkerrigan/riak-ruby-ledger). Another approach could be the client-side implementation of a HAT (Highly Available Transaction) algorithm.
165 |
166 | #### Sets
167 |
168 | Create a set (`alias Riak.CRDT.Set`):
169 |
170 | ```elixir
171 | Set.new
172 | |> Set.put("foo")
173 | |> Set.put("bar")
174 | |> Riak.update("sets", "my_set_bucket", "my_key")
175 | ```
176 |
177 | And fetch the set:
178 |
179 | ```elixir
180 | set = Riak.find("sets", "my_set_bucket", "my_key")
181 | |> Set.value
182 | ```
183 |
184 | Where `set` is an `orddict`.
185 |
186 | #### Maps
187 |
188 | Maps handle binary keys with any other datatype (map, set, flag, register and counter).
189 |
190 | Create a map (`alias Riak.CRDT.Map`):
191 |
192 | ```elixir
193 | register = Register.new("some string")
194 | flag = Flag.new |> Flag.enable
195 | Map.new
196 | |> Map.put("k1", register)
197 | |> Map.put("k2", flag)
198 | |> Riak.update("maps", "my_map_bucket", "map_key")
199 | ```
200 |
201 | And fetch the map:
202 |
203 | ```elixir
204 | map = Riak.find("maps", "my_map_bucket", key) |> Map.value
205 | ```
206 |
207 | Where `map` is an `orddict`.
208 |
209 | ## Examples
210 |
211 | Check the `examples/` directory for a few example elixir applications using the riak client.
212 |
213 | For more functionality, check `test/` directory.
214 |
215 | ## Tests
216 |
217 | ```
218 | MIX_ENV=test mix do deps.get, test
219 | ```
220 |
221 | ## License
222 |
223 | Copyright 2015 Drew Kerrigan.
224 | Copyright 2014 Eduardo Gurgel.
225 |
226 | Licensed under the Apache License, Version 2.0 (the "License");
227 | you may not use this file except in compliance with the License.
228 | You may obtain a copy of the License at
229 |
230 | http://www.apache.org/licenses/LICENSE-2.0
231 |
232 | Unless required by applicable law or agreed to in writing, software
233 | distributed under the License is distributed on an "AS IS" BASIS,
234 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
235 | See the License for the specific language governing permissions and
236 | limitations under the License.
237 |
238 | """
239 | import Riak.Pool
240 | require Record
241 |
242 | @doc """
243 | Ping the server.
244 | """
245 | defpool ping(pid) when is_pid(pid), do: :riakc_pb_socket.ping(pid)
246 |
247 | @doc """
248 | Put the metadata/value in the object under bucket-type/bucket/key.
249 | """
250 | defpool put(pid, obj) when is_pid(pid) do
251 | case :riakc_pb_socket.put(pid, Riak.Object.to_robj(obj)) do
252 | {:ok, new_key} when is_binary(new_key) -> %{obj | key: new_key}
253 | {:ok, new_object} -> %{obj | key: :riakc_obj.key(new_object)}
254 | :ok -> obj
255 | {:error, :notfound} -> nil
256 | {:error, term} -> {:error, term}
257 | end
258 | end
259 |
260 | @doc """
261 | Updates the convergent datatype in Riak with local
262 | modifications stored in the container type.
263 | """
264 | defpool update(pid, datatype, type, bucket, key) when is_pid(pid) do
265 | :riakc_pb_socket.update_type(pid, {type, bucket},
266 | key, to_op(datatype))
267 | end
268 |
269 | defp to_op(datatype) do
270 | case datatype do
271 | datatype when Record.is_record(datatype, :set) ->
272 | :riakc_set.to_op(datatype)
273 | datatype when Record.is_record(datatype, :counter) ->
274 | :riakc_counter.to_op(datatype)
275 | datatype when Record.is_record(datatype, :map) ->
276 | :riakc_map.to_op(datatype)
277 | datatype when Record.is_record(datatype, :hll) ->
278 | :riakc_hll.to_op(datatype)
279 | _ -> :undefined
280 | end
281 | end
282 |
283 | @doc """
284 | Get bucket-type/bucket/key from the server.
285 | """
286 | defpool find(pid, bucket, key) when is_pid(pid) do
287 | case :riakc_pb_socket.get(pid, bucket, key) do
288 | {:ok, object} ->
289 | if :riakc_obj.value_count(object) > 1 do
290 | build_sibling_list(:riakc_obj.get_contents(object),[])
291 | else
292 | Riak.Object.from_robj(object)
293 | end
294 | {:error, :notfound} -> nil
295 | {:error, term} -> {:error, term}
296 | end
297 | end
298 |
299 | @doc """
300 | Fetches the representation of a convergent datatype from Riak.
301 |
302 | TODO: In the current implementation, it's very easy to confuse working
303 | with regular k/v objects and datatypes. Clarify so that these aren't
304 | conflated by assuming that any object with a type is a datatype.
305 | """
306 | defpool find(pid, type, bucket, key) when is_pid(pid) do
307 | case :riakc_pb_socket.fetch_type(pid, {type, bucket}, key) do
308 | {:ok, object} -> object
309 | {:error, :notfound} -> nil
310 | {:error, {:notfound, _crdt}} -> nil
311 | {:error, term} -> {:error, term}
312 | end
313 | end
314 |
315 | # [["X-Riak-Deleted" | true]]
316 | defp sibling_deleted?(md) do
317 | presented = md
318 | |> :dict.fetch_keys
319 | |> Enum.find(& &1 == "X-Riak-Deleted")
320 | presented && :dict.fetch("X-Riak-Deleted", md)
321 | end
322 | defp build_sibling_list([], [final_data] = _final_list), do: %Riak.Object{data: final_data}
323 | defp build_sibling_list([], final_list), do: final_list
324 | defp build_sibling_list([{md, val}|t], final_list) do
325 | build_sibling_list(t, (if sibling_deleted?(md), do: final_list, else: [val|final_list]))
326 | end
327 |
328 | @doc """
329 | Picks the sibling to "win" over the other siblings via a list index.
330 | """
331 | defpool resolve(pid, bucket, key, index) when is_pid(pid) do
332 | case :riakc_pb_socket.get(pid, bucket, key) do
333 | {:ok, object} ->
334 | new_object = :riakc_obj.select_sibling(index, object)
335 | :riakc_pb_socket.put(pid, new_object)
336 | {:error, term} -> {:error, term}
337 | end
338 | end
339 |
340 | @doc """
341 | Delete the key/value.
342 | """
343 | defpool delete(pid, obj) when is_pid(pid), do: delete(pid, obj.bucket, obj.key)
344 | defpool delete(pid, bucket, key) when is_pid(pid), do: :riakc_pb_socket.delete(pid, bucket, key)
345 | defpool delete(pid, type, bucket, key) when is_pid(pid), do: :riakc_pb_socket.delete(pid, {type, bucket}, key)
346 | end
347 |
--------------------------------------------------------------------------------
/lib/riak/bucket.ex:
--------------------------------------------------------------------------------
1 | defmodule Riak.Bucket do
2 | import Riak.Pool
3 | import :riakc_pb_socket, except: [put: 3, put: 4]
4 |
5 | @doc """
6 | List all buckets on the server with or without server-side timeout.
7 | ***This is a potentially expensive operation and should not be used in production.***
8 | """
9 | defpool list(pid) when is_pid(pid), do: list_buckets(pid)
10 | defpool list(pid, timeout) when is_pid(pid), do: list_buckets(pid, timeout)
11 |
12 | @doc """
13 | List all buckets on the server with or without server-side timeout. Return only buckets.
14 | ***This is a potentially expensive operation and should not be used in production.***
15 | """
16 | defpool list!(pid) when is_pid(pid) do
17 | {:ok, buckets} = list(pid)
18 | buckets
19 | end
20 | defpool list!(pid, timeout) when is_pid(pid) do
21 | {:ok, buckets} = list(pid, timeout)
22 | buckets
23 | end
24 |
25 | @doc """
26 | List all keys in a bucket.
27 | ***This is a potentially expensive operation and should not be used in production.***
28 | """
29 | def keys(pid, type, bucket) when is_pid(pid) and is_binary(bucket), do: keys(pid, {type, bucket})
30 | def keys(pid, type, bucket, timeout) when is_pid(pid), do: keys(pid, {type, bucket}, timeout)
31 | defpool keys(pid, bucket) when is_pid(pid), do: list_keys(pid, bucket)
32 | defpool keys(pid, bucket, timeout) when is_pid(pid), do: list_keys(pid, bucket, timeout)
33 |
34 | @doc """
35 | List all keys in a bucket. Return only buckets.
36 | ***This is a potentially expensive operation and should not be used in production.***
37 | """
38 | def keys!(pid, type, bucket) when is_pid(pid) and is_binary(bucket), do: keys!(pid, {type, bucket})
39 | def keys!(pid, type, bucket, timeout) when is_pid(pid), do: keys!(pid, {type, bucket}, timeout)
40 | defpool keys!(pid, bucket) when is_pid(pid) do
41 | {:ok, keys} = keys(pid, bucket)
42 | keys
43 | end
44 | defpool keys!(pid, bucket, timeout) when is_pid(pid) do
45 | {:ok, keys} = keys(pid, bucket, timeout)
46 | keys
47 | end
48 |
49 | @doc """
50 | Get bucket properties.
51 | """
52 | defpool get(pid, bucket) when is_pid(pid), do: get_bucket(pid, bucket)
53 |
54 | @doc """
55 | Set bucket properties.
56 | """
57 | defpool put(pid, bucket, props) when is_pid(pid), do: set_bucket(pid, bucket, props)
58 | defpool put(pid, bucket, type, props) when is_pid(pid) do
59 | set_bucket(pid, {type, bucket}, props)
60 | end
61 |
62 | @doc """
63 | Reset bucket properties back to the defaults.
64 | """
65 | defpool reset(pid, bucket) when is_pid(pid), do: reset_bucket(pid, bucket)
66 |
67 | defmodule Type do
68 | @doc """
69 | Get properties for bucket type.
70 | """
71 | defpool get(pid, type) when is_pid(pid), do: get_bucket_type(pid, type)
72 |
73 | @doc """
74 | Set properties for bucket type.
75 | """
76 | defpool put(pid, type, props) when is_pid(pid), do: set_bucket_type(pid, type, props)
77 |
78 | defpool list(pid, type) when is_pid(pid) and is_binary(type), do: list_buckets(pid, type)
79 | defpool list(pid, type, timeout) when is_pid(pid), do: list_buckets(pid, type, timeout)
80 |
81 | defpool list!(pid, type) when is_pid(pid) and is_binary(type) do
82 | {:ok, buckets} = list(pid, type)
83 | buckets
84 | end
85 | defpool list!(pid, type, timeout) when is_pid(pid) do
86 | {:ok, buckets} = list(pid, type, timeout)
87 | buckets
88 | end
89 | end
90 |
91 | @doc """
92 | Used to "register" the atoms used by the protobuf interface, not for public use.
93 | """
94 | def possible_props do
95 | [n_val: 3, old_vclock: 86400, young_vclock: 20, big_vclock: 50, small_vclock: 50, allow_mult: false, last_write_wins: false,
96 | basic_quorum: false, notfound_ok: false, precommit: [], postcommit: [], chash_keyfun: {:riak_core_util, :chash_std_keyfun},
97 | linkfun: {:modfun, :riak_kv_wm_link_walker, :mapreduce_linkfun}, pr: 0, r: :quorum, w: :quorum, pw: 0, dw: :quorum,
98 | rw: :quorum]
99 | end
100 | end
101 |
102 |
--------------------------------------------------------------------------------
/lib/riak/connection.ex:
--------------------------------------------------------------------------------
1 | defmodule Riak.Connection do
2 | @doc """
3 | Create a linked process to talk with the riak server on host:port.
4 | """
5 | def start_link(host \\ '127.0.0.1', port \\ 8087, args \\ []) do
6 | :riakc_pb_socket.start_link(host, port, args)
7 | end
8 |
9 | @doc """
10 | Create a process to talk with the riak server on host:port.
11 | """
12 | def start(host \\ '127.0.0.1', port \\ 8087, args \\ []) do
13 | :riakc_pb_socket.start(host, port, args)
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/riak/crdt.ex:
--------------------------------------------------------------------------------
1 | defmodule Riak.CRDT do
2 | @moduledoc """
3 | Common CRDT module
4 | """
5 | require Record
6 |
7 | Enum.each [:set, :map, :counter, :register, :flag, :hll], fn t ->
8 | def type(value) when Record.is_record(value, unquote(t)), do: unquote(t)
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/lib/riak/crdt/counter.ex:
--------------------------------------------------------------------------------
1 | defmodule Riak.CRDT.Counter do
2 | @moduledoc """
3 | Counter data-type on Riak 2.0.
4 | """
5 | require Record
6 |
7 | @doc """
8 | Create a new counter
9 | """
10 | def new(), do: :riakc_counter.new()
11 |
12 | @doc """
13 | Increment a `counter` on the `amount` defaulting in 1
14 | """
15 | def increment(counter, amount \\ 1)
16 | def increment(counter, amount) when Record.is_record(counter, :counter) do
17 | :riakc_counter.increment(amount, counter)
18 | end
19 | def increment(nil, _), do: {:error, :nil_object}
20 | def increment({:error, term}, _), do: {:error, term}
21 |
22 | @doc """
23 | Decrement a `counter` on the `amount` defaulting in 1
24 | """
25 | def decrement(counter, amount \\ 1)
26 | def decrement(counter, amount) when Record.is_record(counter, :counter) do
27 | :riakc_counter.increment(-amount, counter)
28 | end
29 | def decrement(nil, _), do: {:error, :nil_object}
30 | def decrement({:error, term}, _), do: {:error, term}
31 |
32 | @doc """
33 | Get the original value as a number
34 | """
35 | def value(counter) when Record.is_record(counter, :counter) do
36 | :riakc_counter.value(counter)
37 | end
38 | def value(nil), do: {:error, :nil_object}
39 | def value({:error, term}), do: {:error, term}
40 | end
41 |
--------------------------------------------------------------------------------
/lib/riak/crdt/flag.ex:
--------------------------------------------------------------------------------
1 | defmodule Riak.CRDT.Flag do
2 | @moduledoc """
3 | Encapsulates a boolean datatype inside a CRDT.Map
4 | """
5 | require Record
6 |
7 | @doc """
8 | Creates a new flag container
9 | """
10 | def new(), do: :riakc_flag.new()
11 | def new(context), do: :riakc_flag.new(context)
12 | def new(value, context) when is_boolean(value), do: :riakc_flag.new(value, context)
13 |
14 | @doc """
15 | Extracts current value of `flag`
16 | """
17 | def value(flag) when Record.is_record(flag, :flag) do
18 | :riakc_flag.value(flag)
19 | end
20 | def value(nil), do: {:error, :nil_object}
21 | def value({:error, term}), do: {:error, term}
22 |
23 | @doc """
24 | Turns the value to true
25 | """
26 | def enable(flag) when Record.is_record(flag, :flag) do
27 | :riakc_flag.enable(flag)
28 | end
29 | def enable(nil), do: {:error, :nil_object}
30 | def enable({:error, term}), do: {:error, term}
31 |
32 | @doc """
33 | Turns the value to false
34 | """
35 | def disable(flag) when Record.is_record(flag, :flag) do
36 | # This is not ideal, create a :riakc_flag::flag() record with a disable op
37 | case flag do
38 | {:flag, value, _, :undefined} ->
39 | {:flag, value, :disable, :undefined};
40 | _ -> :riakc_flag.disable(flag)
41 | end
42 | end
43 | def disable(nil), do: {:error, :nil_object}
44 | def disable({:error, term}), do: {:error, term}
45 | end
46 |
--------------------------------------------------------------------------------
/lib/riak/crdt/hll.ex:
--------------------------------------------------------------------------------
1 | defmodule Riak.CRDT.HyperLogLog do
2 | @moduledoc """
3 | Encapsulates Riak HLL
4 | """
5 | require Record
6 |
7 | @doc """
8 | Creates a new `hll`
9 | """
10 | def new(), do: :riakc_hll.new()
11 |
12 | @doc """
13 | Adds a distinct element to the HLL
14 | """
15 | def add_element(hll, elm), do: :riakc_hll.add_element(elm, hll)
16 |
17 | @doc """
18 | Adds a list of elements to the HLL
19 | """
20 | def add_elements(nil, _), do: {:error, :nil_object}
21 | def add_elements({:error, term}, _), do: {:error, term}
22 | def add_elements(hll, list) when is_list(list), do: :riakc_hll.add_elements(list, hll)
23 |
24 | @doc """
25 | Fetch the value of the HLL
26 | """
27 | def value(nil), do: {:error, :nil_object}
28 | def value({:error, term}), do: {:error, term}
29 | def value(hll), do: :riakc_hll.value(hll)
30 | end
31 |
--------------------------------------------------------------------------------
/lib/riak/crdt/map.ex:
--------------------------------------------------------------------------------
1 | defmodule Riak.CRDT.Map do
2 | @moduledoc """
3 | Encapsulates Riak maps
4 | """
5 | require Record
6 |
7 | @doc """
8 | Creates a new `map`
9 | """
10 | def new(), do: :riakc_map.new()
11 |
12 | @doc """
13 | Get the `map` size
14 | """
15 | def size(map) when Record.is_record(map, :map), do: :riakc_map.size(map)
16 | def size(nil), do: {:error, :nil_object}
17 | def size({:error, term}), do: {:error, term}
18 |
19 | @doc """
20 | Fetch the value associated to `key` with the `key_type` on `map`
21 | """
22 | def get(map, key_type, key) when Record.is_record(map, :map) do
23 | :riakc_map.fetch({key, key_type}, map)
24 | end
25 | def get(nil, _, _), do: {:error, :nil_object}
26 | def get({:error, term}, _, _), do: {:error, term}
27 |
28 | @doc """
29 | Update the `key` on the `map` by passing the function `fun`
30 | to update the value based on the current value (if exists) as argument
31 | The key_type must be :register, :map, :set, :flag or :counter
32 | """
33 | def update(map, key_type, key, fun) when Record.is_record(map, :map)
34 | and is_atom(key_type)
35 | and is_binary(key)
36 | and is_function(fun, 1) do
37 |
38 | :riakc_map.update({key, key_type}, fun, map)
39 | end
40 | def update(nil, _, _, _), do: {:error, :nil_object}
41 | def update({:error, term}, _, _, _), do: {:error, term}
42 |
43 | @doc """
44 | Update the `key` on the `map` by passing the `value`
45 | The value can be any other CRDT
46 | """
47 | def put(map, key, value) when Record.is_record(map, :map)
48 | and is_binary(key) do
49 | key_type = Riak.CRDT.type(value)
50 | fun = fn _ -> value end
51 | :riakc_map.update({key, key_type}, fun, map)
52 | end
53 | def put(nil, _, _), do: {:error, :nil_object}
54 | def put({:error, term}, _, _), do: {:error, term}
55 |
56 | @doc """
57 | Delete a `key` from the `map`
58 | """
59 | def delete(map, key) when Record.is_record(map, :map) do
60 | :riakc_map.erase(key, map)
61 | end
62 | def delete(nil, _), do: {:error, :nil_object}
63 | def delete({:error, term}, _), do: {:error, term}
64 |
65 | @doc """
66 | Get the original value of the `map`
67 | """
68 | def value(map) when Record.is_record(map, :map), do: :riakc_map.value(map)
69 | def value(nil), do: {:error, :nil_object}
70 | def value({:error, term}), do: {:error, term}
71 |
72 | @doc """
73 | List all keys of the `map`
74 | """
75 | def keys(map) when Record.is_record(map, :map), do: :riakc_map.fetch_keys(map)
76 | def keys(nil), do: {:error, :nil_object}
77 | def keys({:error, term}), do: {:error, term}
78 |
79 | @doc """
80 | Test if the `key` is contained in the `map`
81 | """
82 | def has_key?(map, key) when Record.is_record(map, :map) do
83 | :riakc_map.is_key(key, map)
84 | end
85 | def has_key?(nil, _), do: {:error, :nil_object}
86 | def has_key?({:error, term}, _), do: {:error, term}
87 | end
88 |
--------------------------------------------------------------------------------
/lib/riak/crdt/register.ex:
--------------------------------------------------------------------------------
1 | defmodule Riak.CRDT.Register do
2 | @moduledoc """
3 | Encapsulates a binary data to be used on CRDT.Map's
4 | """
5 | require Record
6 |
7 | @doc """
8 | Creates a new register
9 | """
10 | def new(), do: :riakc_register.new()
11 |
12 | @doc """
13 | Creates a new register with the initial `value`
14 | """
15 | def new(value) when is_binary(value), do: set(new(), value)
16 |
17 | @doc """
18 | Extracts current value of `register`
19 | """
20 | def value(register) when Record.is_record(register, :register) do
21 | :riakc_register.value(register)
22 | end
23 | def value(nil), do: {:error, :nil_object}
24 | def value({:error, term}), do: {:error, term}
25 |
26 | @doc """
27 | Set the `value` on the `register`
28 | """
29 | def set(register, value) when Record.is_record(register, :register)
30 | and is_binary(value) do
31 | :riakc_register.set(value, register)
32 | end
33 | def set(nil, _), do: {:error, :nil_object}
34 | def set({:error, term}, _), do: {:error, term}
35 | end
36 |
--------------------------------------------------------------------------------
/lib/riak/crdt/set.ex:
--------------------------------------------------------------------------------
1 | defmodule Riak.CRDT.Set do
2 | @moduledoc """
3 | Encapsulates riakc_set using elixir Set functions as interface.
4 | It accepts only binary as items.
5 | """
6 | require Record
7 |
8 | @doc """
9 | Create an empty set
10 | """
11 | def new, do: :riakc_set.new
12 | def new(context), do: :riakc_set.new(context)
13 | def new(value, context) when is_list(value), do: :riakc_set.new(value, context)
14 |
15 | @doc """
16 | Get original value as an `ordset`
17 | """
18 | def value(set) when Record.is_record(set, :set) do
19 | :riakc_set.value(set)
20 | end
21 | def value(nil), do: {:error, :nil_object}
22 | def value({:error, term}), do: {:error, term}
23 |
24 | @doc """
25 | Checks if `set` contains `value`.
26 | """
27 | def member?(set, value) when Record.is_record(set, :set) and is_binary(value) do
28 | :riakc_set.is_element(value, set)
29 | end
30 | def member?(nil, _), do: {:error, :nil_object}
31 | def member?({:error, term}, _), do: {:error, term}
32 |
33 | @doc """
34 | Insert `value` on `set`
35 | """
36 | def put(set, value) when Record.is_record(set, :set) and is_binary(value) do
37 | :riakc_set.add_element(value, set)
38 | end
39 | def put(nil, _), do: {:error, :nil_object}
40 | def put({:error, term}, _), do: {:error, term}
41 |
42 | @doc """
43 | Delete `value` on `set`
44 | """
45 | def delete(set, value) when Record.is_record(set, :set) and is_binary(value) do
46 | :riakc_set.del_element(value, set)
47 | end
48 | def delete(nil, _), do: {:error, :nil_object}
49 | def delete({:error, term}, _), do: {:error, term}
50 |
51 | @doc """
52 | Returns the number of elements in `set`
53 | """
54 | def size(set) when Record.is_record(set, :set) do
55 | :riakc_set.size(set)
56 | end
57 | def size(nil), do: {:error, :nil_object}
58 | def size({:error, term}), do: {:error, term}
59 | end
60 |
--------------------------------------------------------------------------------
/lib/riak/index.ex:
--------------------------------------------------------------------------------
1 | defmodule Riak.Index do
2 | import Riak.Pool
3 | import :riakc_pb_socket
4 |
5 | @doc """
6 | @deprecated There are performance issues with 2i, please use another query strategy.
7 | Execute a secondary index equality query.
8 | """
9 | defpool query(pid, bucket, {type, name}, key, opts) when is_pid(pid) do
10 | name = String.to_charlist(name)
11 | response = get_index_eq(pid, bucket, {type, name}, key, opts)
12 | handle_query_response(response)
13 | end
14 |
15 | @doc """
16 | @deprecated There are performance issues with 2i, please use another query strategy.
17 | Execute a secondary index range query.
18 | """
19 | defpool query(pid, bucket, {type, name}, startkey, endkey, opts) when is_pid(pid) do
20 | name = String.to_charlist(name)
21 | response = get_index_range(pid, bucket, {type, name}, startkey, endkey, opts)
22 | handle_query_response(response)
23 | end
24 |
25 | defp handle_query_response(response) do
26 | case response do
27 | {:ok, {:index_results_v1, keys, terms, continuation}} ->
28 | {keys, terms, continuation}
29 | {:error, term} -> {:error, term}
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/lib/riak/map_reduce.ex:
--------------------------------------------------------------------------------
1 | defmodule Riak.Mapred do
2 | import Riak.Pool
3 | import :riakc_pb_socket
4 |
5 | @doc """
6 | @deprecated There are performance issues with MapReduce, please use another query strategy.
7 | Perform a MapReduce job across the cluster.
8 | See the MapReduce documentation for explanation of behavior.
9 | """
10 | defpool query(pid, inputs, query) when is_pid(pid), do: mapred(pid, inputs, query)
11 | defpool query(pid, inputs, query, timeout) when is_pid(pid), do: mapred(pid, inputs, query, timeout)
12 |
13 | defmodule Bucket do
14 | @doc """
15 | @deprecated There are performance issues with MapReduce, please use another query strategy.
16 | Perform a MapReduce job across the cluster using an entire bucket as the input.
17 | See the MapReduce documentation for explanation of behavior.
18 | """
19 | defpool query(pid, bucket, query) when is_pid(pid), do: mapred_bucket(pid, bucket, query)
20 | defpool query(pid, bucket, query, timeout) when is_pid(pid), do: mapred_bucket(pid, bucket, query, timeout)
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/lib/riak/object.ex:
--------------------------------------------------------------------------------
1 | defmodule Riak.Object do
2 | @moduledoc """
3 | The Data wrapper makes it convenient to work with Riak data in Elixir
4 | """
5 |
6 | @doc """
7 | Struct representing a Riak Object. Attributes:
8 | * `type`: String; Bucket Type with a unique name within the cluster namespace
9 | * `bucket`: String; Bucket with a unique name within the bucket type namespace
10 | * `key`: String; Not required; Key with a unique name within the bucket namespace
11 | * `data`: Any; Value to be stored under the key
12 | * `metadata`: Orddict; User specified metadata
13 | * `vclock`: String; Dotted Version Vector / Causal Context for object
14 | * `content_type`: String; Content Type for object
15 | """
16 | defstruct [bucket: nil, type: nil, key: nil, data: nil, metadata: nil, vclock: nil, content_type: "application/json"]
17 |
18 | @doc """
19 | Get all metadata entries
20 | """
21 | def get_metadata(obj, key) do
22 | case :riakc_obj.get_user_metadata_entry(
23 | to_undefined(obj.metadata), to_undefined(key)) do
24 | :notfound -> nil
25 | val -> val
26 | end
27 | end
28 |
29 | def get_all_metadata(obj) do
30 | :riakc_obj.get_user_metadata_entries(
31 | to_undefined(obj.metadata))
32 | end
33 |
34 | def delete_metadata(obj, key) do
35 | %{obj | metadata: :riakc_obj.delete_user_metadata_entry(
36 | to_undefined(obj.metadata), to_undefined(key))}
37 | end
38 |
39 | def delete_all_metadata(obj) do
40 | %{obj | metadata: :riakc_obj.clear_user_metadata_entries(
41 | to_undefined(obj.metadata))}
42 | end
43 |
44 | def put_metadata(obj, {key, value}) do
45 | %{obj | metadata: :riakc_obj.set_user_metadata_entry(
46 | to_undefined(obj.metadata), {key, value})}
47 | end
48 |
49 | # Secondary Index
50 | def index_id({:binary_index, name}), do: "#{name}_bin"
51 | def index_id({:integer_index, name}), do: "#{name}_int"
52 |
53 | def get_index(obj, {type, name}) do
54 | case :riakc_obj.get_secondary_index(
55 | to_undefined(obj.metadata),
56 | {to_undefined(type), to_undefined(name)}) do
57 | :notfound -> nil
58 | val -> val
59 | end
60 | end
61 |
62 | def get_all_indexes(obj) do
63 | :riakc_obj.get_secondary_indexes(to_undefined(obj.metadata))
64 | end
65 |
66 | def delete_index(obj, {type, name}) do
67 | %{obj | metadata: :riakc_obj.delete_secondary_index(
68 | to_undefined(obj.metadata),
69 | {to_undefined(type), to_undefined(name)})}
70 | end
71 |
72 | def delete_all_indexes(obj) do
73 | %{obj | metadata: :riakc_obj.clear_secondary_indexes(
74 | to_undefined(obj.metadata))}
75 | end
76 |
77 | def put_index(obj, {type, name}, values) do
78 | %{obj | metadata: :riakc_obj.add_secondary_index(
79 | to_undefined(obj.metadata),
80 | [{{to_undefined(type), to_undefined(name)},
81 | to_undefined(values)}])}
82 | end
83 |
84 | # Links
85 | def get_link(obj, tag) do
86 | case :riakc_obj.get_links(
87 | to_undefined(obj.metadata), tag) do
88 | :notfound -> nil
89 | val -> val
90 | end
91 | end
92 |
93 | def get_all_links(obj) do
94 | :riakc_obj.get_all_links(
95 | to_undefined(obj.metadata))
96 | end
97 |
98 | def delete_link(obj, tag) do
99 | %{obj | metadata: :riakc_obj.delete_links(
100 | to_undefined(obj.metadata), tag)}
101 | end
102 | def delete_all_links(obj) do
103 | %{obj | metadata: :riakc_obj.clear_links(
104 | to_undefined(obj.metadata))}
105 | end
106 |
107 | def put_link(obj, tag, bucket, key) do
108 | %{obj | metadata: :riakc_obj.add_link(
109 | to_undefined(obj.metadata),
110 | [{tag, [{to_undefined(bucket),to_undefined(key)}]}])}
111 | end
112 |
113 | def from_robj(robj) do
114 | data =
115 | try do
116 | :riakc_obj.get_update_value(robj)
117 | catch
118 | :no_value -> nil
119 | e -> raise e
120 | end
121 | %Riak.Object{bucket: to_nil(:riakc_obj.bucket(robj)),
122 | type: to_nil(:riakc_obj.bucket_type(robj)),
123 | key: to_nil(:riakc_obj.key(robj)),
124 | data: to_nil(data),
125 | metadata: to_nil(:riakc_obj.get_update_metadata(robj)),
126 | vclock: to_nil(:riakc_obj.vclock(robj)),
127 | content_type: to_nil(:riakc_obj.get_update_content_type(robj))}
128 | end
129 |
130 | def to_robj(obj) do
131 | type = to_undefined(obj.type)
132 | bucket = to_bucket(obj.bucket)
133 | typed_bucket =
134 | case {type, bucket} do
135 | {:undefined, b} -> b;
136 | {t, {_, b}} -> {t, b};
137 | {t, b} -> {t, b};
138 | end
139 | robj = :riakc_obj.new(
140 | typed_bucket,
141 | to_undefined(obj.key),
142 | to_undefined(obj.data),
143 | to_undefined(obj.content_type))
144 | robj =
145 | case to_undefined(obj.vclock) do
146 | :undefined -> robj;
147 | v -> :riakc_obj.set_vclock(robj, v)
148 | end
149 | robj =
150 | case to_undefined(obj.metadata) do
151 | :undefined -> robj;
152 | m -> :riakc_obj.update_metadata(robj, m)
153 | end
154 |
155 | robj
156 | end
157 |
158 | def create(args \\ [bucket: "default"]) do
159 | obj = struct(Riak.Object, args)
160 | from_robj(to_robj(obj))
161 | end
162 |
163 | defp to_undefined(nil) do
164 | :undefined
165 | end
166 | defp to_undefined(v) do
167 | v
168 | end
169 |
170 | defp to_nil(:undefined) do
171 | nil
172 | end
173 | defp to_nil(v) do
174 | v
175 | end
176 |
177 | defp to_bucket({nil, b}) do
178 | to_undefined(b)
179 | end
180 | defp to_bucket({:undefined, b}) do
181 | to_undefined(b)
182 | end
183 | defp to_bucket(b) do
184 | to_undefined(b)
185 | end
186 |
187 |
188 | end
189 |
--------------------------------------------------------------------------------
/lib/riak/pool.ex:
--------------------------------------------------------------------------------
1 | defmodule Riak.Pool do
2 | @moduledoc """
3 | [EXPERIMENTAL]
4 | This module adds defpool to define functions with a
5 | lower arity for each function so:
6 |
7 | Riak.put(pid, bucket, key, data) ->
8 |
9 | - Riak.put(bucket, key, data) that calls the previous function
10 | with a pid from the pool
11 |
12 | - Riak.put(pool_group, bucket, key, data) when is_atom(pool_group)
13 | that calls the previous function
14 | with a pid from the given pool name from the pool, instead of the default :riak group
15 |
16 |
17 | """
18 | defmacro defpool(args, do: block) do
19 | {{name, _, args}, guards} = extract_guards(args)
20 | [_pid_arg | other_args] = args
21 | has_guards = (guards != [])
22 | quote do
23 | def unquote(name)(group_name,unquote_splicing(other_args)) when is_atom(group_name) do
24 | pid = take_group_member(group_name, 500)
25 | case pid do
26 | :error_no_members -> :connection_pool_exhausted
27 | _ ->
28 | result = unquote(name)(pid, unquote_splicing(other_args))
29 | :pooler.return_group_member(:riak, pid, :ok)
30 | result
31 | end
32 | end
33 | if unquote(has_guards) do
34 | def unquote(name)(unquote_splicing(args)) when unquote(hd(guards)) do
35 | unquote(block)
36 | end
37 | else
38 | def unquote(name)(unquote_splicing(args)) do
39 | unquote(block)
40 | end
41 | end
42 | def unquote(name)(unquote_splicing(other_args)) do
43 | pid = take_group_member(:riak, 500)
44 | case pid do
45 | :error_no_members -> :connection_pool_exhausted
46 | _ ->
47 | result = unquote(name)(pid, unquote_splicing(other_args))
48 | :pooler.return_group_member(:riak, pid, :ok)
49 | result
50 | end
51 | end
52 | end
53 | end
54 |
55 | def take_group_member(group_name, timeout \\ 100)
56 | def take_group_member(_, timeout) when timeout <= 0 do
57 | :error_no_members
58 | end
59 | def take_group_member(group_name, timeout) do
60 | case :pooler.take_group_member(group_name) do
61 | :error_no_members ->
62 | #should probably log a warning / error here so the operator knows something is wrong
63 | :timer.sleep(100)
64 | take_group_member(group_name, timeout - 100)
65 | pid -> pid
66 | end
67 | end
68 |
69 | defp extract_guards({:when, _, [left, right]}), do: {left, extract_or_guards(right)}
70 | defp extract_guards(else_), do: {else_, []}
71 | defp extract_or_guards({:when, _, [left, right]}), do: [left|extract_or_guards(right)];
72 | defp extract_or_guards(term), do: [term]
73 |
74 | end
75 |
--------------------------------------------------------------------------------
/lib/riak/search.ex:
--------------------------------------------------------------------------------
1 | defmodule Riak.Search do
2 | import Riak.Pool
3 | import :riakc_pb_socket, except: [put: 2, put: 3, put: 4]
4 |
5 | defpool query(pid, index, query) when is_pid(pid), do: search(pid, index, query)
6 | defpool query(pid, index, query, options) when is_pid(pid), do: search(pid, index, query, options)
7 | defpool query(pid, index, query, options, timeout) when is_pid(pid), do: search(pid, index, query, options, timeout)
8 |
9 | defmodule Index do
10 | defpool list(pid) when is_pid(pid), do: list_search_indexes(pid)
11 | defpool put(pid, index) when is_pid(pid), do: create_search_index(pid, index)
12 | defpool put(pid, index, schema) when is_pid(pid), do: create_search_index(pid, index, schema, [])
13 | defpool put(pid, index, schema, props) when is_pid(pid), do: create_search_index(pid, index, schema, props)
14 | defpool get(pid, index) when is_pid(pid), do: get_search_index(pid, index)
15 | defpool delete(pid, index) when is_pid(pid), do: delete_search_index(pid, index)
16 | end
17 |
18 | defmodule Schema do
19 | defpool get(pid, schema) when is_pid(pid), do: get_search_schema(pid, schema)
20 |
21 | defpool create(pid, schema, content) when is_pid(pid) do
22 | create_search_schema(pid, schema, content)
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/lib/riak/timeseries.ex:
--------------------------------------------------------------------------------
1 | defmodule Riak.Timeseries do
2 | import Riak.Pool
3 |
4 | defpool query(pid, query_text) when is_pid(pid) and is_binary(query_text) do
5 | query_list = :erlang.binary_to_list(query_text)
6 | case :riakc_ts.query(pid, query_list) do
7 | {:ok, results} -> results;
8 | err -> err
9 | end
10 | end
11 |
12 | defpool put(pid, table, data) when is_pid(pid) and is_list(data)do
13 | :riakc_ts.put(pid, table, data)
14 | end
15 |
16 | defpool get(pid, table, key) when is_pid(pid) and is_list(key) do
17 | get(pid, table, key, [])
18 | end
19 |
20 | defpool get(pid, table, key, options) when is_pid(pid) and is_list(key) do
21 | case :riakc_ts.get(pid, table, key, options) do
22 | {:ok, results} -> results;
23 | err -> err
24 | end
25 | end
26 |
27 | defpool delete(pid, table, key) when is_pid(pid) and is_list(key) do
28 | delete(pid, table, key, [])
29 | end
30 |
31 | defpool delete(pid, table, key, options) when is_pid(pid) and is_list(key) do
32 | :riakc_ts.delete(pid, table, key, options)
33 | end
34 |
35 | defpool list!(pid, table) when is_pid(pid) do
36 | list!(pid, table, [])
37 | end
38 |
39 | defpool list!(pid, table, options) when is_pid(pid) do
40 | {:ok, req_id} = :riakc_ts.stream_list_keys(pid, table, options)
41 | wait_for_list(req_id, [])
42 | end
43 |
44 | defp wait_for_list(req_id, acc) do
45 | receive do
46 | {^req_id, :done} -> List.flatten(acc)
47 | {^req_id, {:error, reason}} -> {:error, reason}
48 | {^req_id, {_, res}} -> wait_for_list(req_id, [res|acc])
49 | end
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule Riak.Mixfile do
2 | use Mix.Project
3 |
4 | def project do
5 | [ app: :riak,
6 | version: "1.1.6",
7 | build_per_environment: false,
8 | name: "Riak Elixir Client",
9 | source_url: "https://github.com/drewkerrigan/riak-elixir-client",
10 | deps: deps(),
11 | description: description(),
12 | package: package() ]
13 | end
14 |
15 | # Configuration for the OTP application
16 | def application do
17 | [ applications: [ :pooler ],
18 | included_applications: [ :riakc ] ]
19 | end
20 |
21 | defp deps do
22 | [ {:ex_doc, ">= 0.0.0", only: :dev},
23 | {:earmark, ">= 0.0.0", only: :dev},
24 | {:pooler, "~> 1.5.3"},
25 | {:riakc, "~> 2.5.3"} ]
26 | end
27 |
28 | defp description do
29 | """
30 | A Riak client written in Elixir.
31 | """
32 | end
33 |
34 | defp package do
35 | [ files: ~w(config doc examples lib test LICENSE mix.exs mix.lock README.md THANKS),
36 | maintainers: [ "Drew Kerrigan", "Eduardo Gurgel", "Randy Secrist" ],
37 | licenses: [ "Apache 2.0" ],
38 | links: %{"GitHub" => "https://github.com/drewkerrigan/riak-elixir-client",
39 | "Documentation" => "http://hexdocs.pm/riak"} ]
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/mix.lock:
--------------------------------------------------------------------------------
1 | %{"earmark": {:hex, :earmark, "1.2.3", "206eb2e2ac1a794aa5256f3982de7a76bf4579ff91cb28d0e17ea2c9491e46a4", [:mix], [], "hexpm"},
2 | "ex_doc": {:hex, :ex_doc, "0.16.2", "3b3e210ebcd85a7c76b4e73f85c5640c011d2a0b2f06dcdf5acdb2ae904e5084", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"},
3 | "hamcrest": {:hex, :basho_hamcrest, "0.4.1", "fb7b2c92d252a1e9db936750b86089addaebeb8f87967fb4bbdda61e8863338e", [:make, :mix, :rebar3], [], "hexpm"},
4 | "linguist": {:hex, :linguist, "0.1.5", "98739fc47ac5d951322a57c787f46786c95b53d800105d43a0631633e7f9da9b", [:mix], [], "hexpm"},
5 | "meck": {:hex, :meck, "0.8.4", "59ca1cd971372aa223138efcf9b29475bde299e1953046a0c727184790ab1520", [:rebar, :make], []},
6 | "pooler": {:hex, :pooler, "1.5.3", "898cd1fa301fc42d4a8ed598ce139b71ca85b54c16ab161152b5cc5fbdcfa1a8", [:rebar3], [], "hexpm"},
7 | "protobuffs": {:hex, :protobuffs, "0.8.4", "d38ca5f7380d8477c274680273372011890f8d0037c0d7e7db5c0207b89a4e0b", [:rebar, :make], [{:meck, "~> 0.8.4", [hex: :meck, optional: false]}]},
8 | "riak_pb": {:hex, :riak_pb, "2.3.2", "48ffbf66dbb3f136ab9a7134bac4e496754baa5ef58c4f50a61326736d996390", [:make, :mix, :rebar3], [{:hamcrest, "~> 0.4.1", [hex: :basho_hamcrest, repo: "hexpm", optional: false]}], "hexpm"},
9 | "riakc": {:hex, :riakc, "2.5.3", "6132d9e687a0dfd314b2b24c4594302ca8b55568a5d733c491d8fb6cd4004763", [:make, :mix, :rebar3], [{:riak_pb, "~> 2.3", [hex: :riak_pb, repo: "hexpm", optional: false]}], "hexpm"}}
10 |
--------------------------------------------------------------------------------
/test/bucket_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Riak.BucketTest do
2 | use Riak.Case
3 |
4 | @moduletag :riak1
5 | @tag timeout: 10000
6 |
7 | test "list bucket", context do
8 | {:ok, buckets} = Riak.Bucket.list context[:pid]
9 | assert is_list(buckets)
10 | end
11 |
12 | test "list! bucket", context do
13 | buckets = Riak.Bucket.list! context[:pid]
14 | assert is_list(buckets)
15 | end
16 |
17 | test "list keys", context do
18 | {:ok, keys} = Riak.Bucket.keys context[:pid], "user"
19 | assert is_list(keys)
20 | end
21 |
22 | test "list! keys", context do
23 | keys = Riak.Bucket.keys! context[:pid], "user"
24 | assert is_list(keys)
25 | end
26 |
27 | test "bucket props", context do
28 | pid = context[:pid]
29 | assert :ok == Riak.Bucket.put pid, "user", [{:notfound_ok, false}]
30 |
31 | {:ok, props} = Riak.Bucket.get pid, "user"
32 | assert is_list(props)
33 | assert props[:notfound_ok] == false
34 |
35 | assert :ok == Riak.Bucket.reset pid, "user"
36 |
37 | {:ok, props} = Riak.Bucket.get pid, "user"
38 | assert props[:notfound_ok] == true
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/test/crdt_counter_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Riak.CRDT.CounterTest do
2 | use Riak.Case
3 | alias Riak.CRDT.Counter
4 |
5 | @moduletag :riak2
6 |
7 | test "create, update and find a counter" do
8 | key = Riak.Helper.random_key
9 |
10 | Counter.new
11 | |> Counter.increment
12 | |> Counter.increment(2)
13 | |> Riak.update("counters", "bucketcounter", key)
14 |
15 | counter = Riak.find("counters", "bucketcounter", key)
16 | |> Counter.value
17 |
18 | assert counter == 3
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/test/crdt_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Riak.CRDTTest do
2 | use ExUnit.Case
3 | import Riak.CRDT
4 |
5 | @moduletag :riak2
6 |
7 | test 'type of Register' do
8 | assert type(Riak.CRDT.Register.new) == :register
9 | end
10 |
11 | test 'type of Set' do
12 | assert type(Riak.CRDT.Set.new) == :set
13 | end
14 |
15 | test 'type of Map' do
16 | assert type(Riak.CRDT.Map.new) == :map
17 | end
18 |
19 | test 'type of Flag' do
20 | assert type(Riak.CRDT.Flag.new) == :flag
21 | end
22 |
23 | test 'type of Counter' do
24 | assert type(Riak.CRDT.Counter.new) == :counter
25 | end
26 |
27 | test 'type of HyperLogLog' do
28 | assert type(Riak.CRDT.HyperLogLog.new) == :hll
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/test/flag_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Riak.CRDT.FlagTest do
2 | use Riak.Case
3 | alias Riak.CRDT.Map, as: RiakMap
4 | alias Riak.CRDT.Flag
5 |
6 | @moduletag :riak2
7 |
8 | test "create a flag" do
9 | assert {:flag, false, :undefined, :undefined} = Flag.new
10 | end
11 |
12 | test "create and enable a flag" do
13 | flag =
14 | RiakMap.new
15 | |> RiakMap.put("foo", Flag.new)
16 | |> RiakMap.update(:flag, "foo", &Flag.enable/1)
17 |
18 | assert flag == {
19 | :map, [],
20 | [{{"foo", :flag}, {:flag, false, :enable, :undefined}}],
21 | [],
22 | :undefined
23 | }
24 | end
25 |
26 | test "create and disable a flag" do
27 | flag =
28 | RiakMap.new
29 | |> RiakMap.put("foo", Flag.new("somecontext"))
30 | |> RiakMap.update(:flag, "foo", &Flag.disable/1)
31 |
32 | assert flag == {
33 | :map, [],
34 | [{{"foo", :flag}, {:flag, false, :disable, "somecontext"}}],
35 | [],
36 | :undefined
37 | }
38 | end
39 |
40 | test "create and enable a flag, save then get value" do
41 | RiakMap.new
42 | |> RiakMap.put("flag_key_true", Flag.new |> Flag.enable)
43 | |> Riak.update("maps", "flagbucket", "flagmap")
44 |
45 | flag_value = Riak.find("maps", "flagbucket", "flagmap")
46 | |> RiakMap.get(:flag, "flag_key_true")
47 |
48 | assert flag_value
49 | end
50 |
51 | test "create and disable a flag, save then get value" do
52 | RiakMap.new
53 | |> RiakMap.put("flag_key_false", Flag.new |> Flag.enable)
54 | |> Riak.update("maps", "flagbucket", "flagmap2")
55 |
56 | Riak.find("maps", "flagbucket", "flagmap2")
57 | |> RiakMap.update(:flag, "flag_key_false", &Flag.disable/1)
58 | |> Riak.update("maps", "flagbucket", "flagmap2")
59 |
60 | flag3 = Riak.find("maps", "flagbucket", "flagmap2")
61 |
62 | # Assert flag is false after we saved the disabled one
63 | assert RiakMap.get(flag3, :flag, "flag_key_false") == false
64 | end
65 |
66 | end
67 |
--------------------------------------------------------------------------------
/test/hll_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Riak.CRDT.HyperLogLogTest do
2 | use Riak.Case
3 | alias Riak.CRDT.HyperLogLog
4 |
5 | @moduletag :riak2
6 |
7 | test "create, update and find a hll" do
8 | key = Riak.Helper.random_key
9 |
10 | HyperLogLog.new
11 | |> HyperLogLog.add_element("One")
12 | |> HyperLogLog.add_elements(["Two", "Three", "One", "Two"])
13 | |> Riak.update("hll", "buckethll", key)
14 |
15 | counter = Riak.find("hll", "buckethll", key)
16 | |> HyperLogLog.value
17 |
18 | assert counter == 3
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/test/map_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Riak.CRDT.MapTest do
2 | require IEx
3 | use Riak.Case
4 | alias Riak.CRDT.Map
5 | alias Riak.CRDT.Register
6 | alias Riak.CRDT.Flag
7 | alias Riak.CRDT.Counter
8 | alias Riak.CRDT.Set
9 |
10 | @moduletag :riak2
11 |
12 | test "create, update and find a map with other CRDTs" do
13 | key = Riak.Helper.random_key
14 |
15 | reg_data = "Register data"
16 | reg = Register.new(reg_data)
17 | reg_key = "register_key"
18 |
19 | flag = Flag.new |> Flag.enable
20 | flag_key = "flag_key"
21 |
22 | counter = Counter.new |> Counter.increment
23 | counter_key = "counter_key"
24 |
25 | set = Set.new |> Set.put("foo")
26 | set_key = "set_key"
27 |
28 | Map.new
29 | |> Map.put(reg_key, reg)
30 | |> Map.put(flag_key, flag)
31 | |> Map.update(:counter, counter_key, fn _ -> counter end)
32 | |> Map.put(set_key, set)
33 | |> Riak.update("maps", "bucketmap", key)
34 |
35 | map = Riak.find("maps", "bucketmap", key)
36 | |> Map.value
37 |
38 | map_keys = :orddict.fetch_keys(map)
39 | assert {"counter_key", :counter} in map_keys
40 | assert {"flag_key", :flag} in map_keys
41 | assert {"register_key", :register} in map_keys
42 | assert {"set_key", :set} in map_keys
43 |
44 | assert :orddict.size(map) == 4
45 |
46 | data = :orddict.to_list(map)
47 | assert {{reg_key, :register}, reg_data} in data
48 | assert {{flag_key, :flag}, true} in data
49 | assert {{counter_key, :counter}, 1} in data
50 | assert {{set_key, :set}, ["foo"]} in data
51 | end
52 |
53 | test "create, update and find nested maps" do
54 | key = Riak.Helper.random_key
55 |
56 | flag = Flag.new |> Flag.enable
57 | flag_key = "flag_key"
58 |
59 | nested = Map.new |> Map.put(flag_key, flag)
60 | nested_key = "nested_key"
61 |
62 | Map.new
63 | |> Map.put(nested_key, nested)
64 | |> Riak.update("maps", "bucketmap", key)
65 |
66 | map = Riak.find("maps", "bucketmap", key)
67 |
68 | value_map = map |> Map.value
69 |
70 | assert :orddict.size(value_map) == 1
71 | assert :orddict.fetch({nested_key, :map}, value_map) == [{{flag_key, :flag}, true}]
72 |
73 | exists = Riak.find("maps", "bucketmap", key)
74 | |> Map.has_key?({nested_key, :map})
75 |
76 | assert exists == true
77 |
78 | Riak.find("maps", "bucketmap", key) |> Map.delete({nested_key, :map})
79 | |> Riak.update("maps", "bucketmap", key)
80 |
81 | exists = Riak.find("maps", "bucketmap", key)
82 | |> Map.has_key?({nested_key, :map})
83 |
84 | assert exists == false
85 |
86 | end
87 |
88 | test "create, update, delete map" do
89 | key = Riak.Helper.random_key
90 |
91 | Map.new
92 | |> Map.put("register_key", Register.new("Some Data"))
93 | |> Riak.update("maps", "users", key)
94 |
95 | reg_data = Riak.find("maps", "users", key)
96 | |> Map.get(:register, "register_key")
97 |
98 | assert "Some Data" == reg_data
99 |
100 | Riak.delete("maps", "users", key)
101 | assert Riak.find("maps", "users", key) == nil
102 |
103 | end
104 |
105 | test "map key exists" do
106 | key = Riak.Helper.random_key
107 |
108 | Map.new
109 | |> Map.put("register_key", Register.new("Some Data"))
110 | |> Riak.update("maps", "users", key)
111 |
112 | exists = Riak.find("maps", "users", key)
113 | |> Map.has_key?({"nothere", :register})
114 |
115 | assert exists == false
116 |
117 | exists = Riak.find("maps", "users", key)
118 | |> Map.has_key?({"register_key", :register})
119 |
120 | assert exists == true
121 |
122 | keys = Riak.find("maps", "users", key)
123 | |> Map.keys()
124 |
125 | assert keys == [{"register_key", :register}]
126 | end
127 | end
128 |
--------------------------------------------------------------------------------
/test/pool_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Riak.PoolTest do
2 | use Riak.Case
3 | import Riak.Helper
4 |
5 | @moduletag :riak1
6 |
7 | test "put" do
8 | key = random_key()
9 |
10 | o = Riak.Object.create(bucket: "user", key: key, data: "Drew Kerrigan")
11 |
12 | assert Riak.put(o) == o
13 | assert Riak.put(:riak, o) == o
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/test/riak_test.exs:
--------------------------------------------------------------------------------
1 | defmodule RiakTest do
2 | use Riak.Case
3 |
4 | @moduletag :riak1
5 |
6 | test "put", context do
7 | pid = context[:pid]
8 | key = Riak.Helper.random_key
9 |
10 | o = Riak.Object.create(bucket: "user", key: key, data: "Drew Kerrigan")
11 |
12 | assert Riak.put(pid, o) == o
13 | end
14 |
15 | test "find", context do
16 | pid = context[:pid]
17 | key = Riak.Helper.random_key
18 |
19 | data = "Drew Kerrigan"
20 | o = Riak.Object.create(bucket: "user", key: key, data: data)
21 | Riak.put(pid, o)
22 |
23 | assert Riak.find(pid, "user", key).data == o.data
24 | end
25 |
26 | # curl -XDELETE http://localhost:8098/types/spot/buckets/active/keys/EURUSD
27 | @tag :skip
28 | test "find with deleted", context do
29 |
30 | pid = context[:pid]
31 | key = Riak.Helper.random_key
32 | data = "Drew_Kerrigan"
33 |
34 | target = "http://localhost:8098/types/user_types/buckets/user/keys"
35 | System.cmd("curl", ~w[-s -XPUT #{target}/#{key} -d {"value_s":"#{data}_1"} -H Content-Type:application/json])
36 | System.cmd("curl", ~w[-s -XDELETE #{target}/#{key}])
37 | System.cmd("curl", ~w[-s -XPUT #{target}/#{key} -d {"value_s":"#{data}_2"} -H Content-Type:application/json])
38 |
39 | assert Riak.find(pid, {"user_types", "user"}, key).data == ~s[{"value_s":"#{data}_2"}]
40 | end
41 |
42 | test "delete", context do
43 | pid = context[:pid]
44 | key = Riak.Helper.random_key
45 |
46 | o = Riak.Object.create(bucket: "user", key: key, data: "Drew Kerrigan")
47 | Riak.put(pid, o)
48 |
49 | assert Riak.delete(pid, o) == :ok
50 | end
51 |
52 | test "crud operations and siblings", context do
53 | pid = context[:pid]
54 | key = Riak.Helper.random_key
55 |
56 | o = Riak.Object.create(bucket: "user", key: key, data: "Drew Kerrigan")
57 | u = Riak.put(pid, o)
58 |
59 | assert u != nil
60 |
61 | assert :ok == Riak.delete pid, "user", u.key
62 |
63 | u = Riak.Object.create(bucket: "user", data: "Drew Kerrigan")
64 | assert u.key == nil
65 | u = Riak.put pid, u
66 | assert u.key != nil
67 |
68 | # Get the object again so we don't create a sibling
69 | u = Riak.find pid, "user", u.key
70 |
71 | o = %{u | data: "Something Else"}
72 | u = Riak.put pid, o
73 |
74 | unewdata = Riak.find pid, "user", u.key
75 |
76 | assert unewdata.data == "Something Else"
77 |
78 | assert :ok == Riak.delete pid, "user", u.key
79 | assert :ok == Riak.delete pid, "user", key
80 |
81 | assert nil == Riak.find pid, "user", key
82 | end
83 |
84 | # test "create siblings", context do
85 | # pid = context[:pid]
86 | # key = Riak.Helper.random_key
87 |
88 | # o = Riak.Object.create(type: "siblings", bucket: "user", key: key, data: "Drew Kerrigan")
89 | # Riak.put(pid, o)
90 | # Riak.put(pid, o)
91 | # u = Riak.find(pid, {"siblings", "user"}, key)
92 | # u = case u do
93 | # nil -> Riak.find(pid, {"siblings", "user"}, key)
94 | # u -> u
95 | # end
96 | # assert u != nil
97 | # assert is_list(u)
98 | # Riak.resolve(pid, {"siblings", "user"}, key, Riak.Helper.index_of("Drew Kerrigan", u))
99 | # u = Riak.find(pid, {"siblings", "user"}, key)
100 | # assert u != nil
101 | # assert u.data == "Drew Kerrigan"
102 | # assert :ok == Riak.delete pid, u
103 | # end
104 |
105 | test "user metadata", context do
106 | pid = context[:pid]
107 | key = Riak.Helper.random_key
108 |
109 | mdtest = Riak.Object.create(bucket: "user", key: key, data: "Drew Kerrigan")
110 | |> Riak.Object.put_metadata({"my_key", "my_value"})
111 | |> Riak.Object.put_metadata({"my_key2", "my_value2"})
112 |
113 | mdtest = Riak.put(pid, mdtest)
114 | |> Riak.Object.get_metadata("my_key")
115 |
116 | assert mdtest == "my_value"
117 |
118 | u = Riak.find pid, "user", key
119 |
120 | mdtest2 = u
121 | |> Riak.Object.get_metadata("my_key2")
122 |
123 | assert mdtest2 == "my_value2"
124 |
125 | mdtest3 = u
126 | |> Riak.Object.get_all_metadata()
127 | |> is_list
128 |
129 | assert mdtest3
130 |
131 | u = Riak.Object.delete_metadata(u, "my_key")
132 |
133 | assert nil == Riak.Object.get_metadata(u, "my_key")
134 | assert "my_value2" == Riak.Object.get_metadata(u, "my_key2")
135 |
136 | u = Riak.Object.delete_all_metadata(u)
137 |
138 | assert nil == Riak.Object.get_metadata(u, "my_key2")
139 | assert [] == Riak.Object.get_all_metadata(u)
140 | end
141 |
142 | test "secondary indexes", context do
143 | pid = context[:pid]
144 | key = Riak.Helper.random_key
145 |
146 | o = Riak.Object.create(bucket: "user", key: key, data: "Drew Kerrigan")
147 | |> Riak.Object.put_index({:binary_index, "first_name"}, ["Drew"])
148 | |> Riak.Object.put_index({:binary_index, "last_name"}, ["Kerrigan"])
149 | Riak.put(pid, o)
150 |
151 | assert Riak.Object.get_index(o, {:binary_index, "first_name"}) == ["Drew"]
152 |
153 | case Riak.Index.query(pid, "user", {:binary_index, "first_name"}, "Drew", []) do
154 | {:error, "{error,{indexes_not_supported,riak_kv_bitcask_backend}}"} -> :ok
155 | {keys, terms, continuation} ->
156 | assert is_list(keys)
157 | assert terms == :undefined
158 | assert continuation == :undefined
159 | {keys, terms, continuation} = Riak.Index.query(pid, "user", {:binary_index, "last_name"}, "Kerrigam", "Kerrigao", [])
160 | assert is_list(keys)
161 | assert terms == :undefined
162 | assert continuation == :undefined
163 | end
164 |
165 | o = Riak.Object.delete_index(o, {:binary_index, "first_name"})
166 | Riak.put(pid, o)
167 |
168 | assert Riak.Object.get_index(o, {:binary_index, "first_name"}) == nil
169 |
170 | assert is_list(Riak.Object.get_all_indexes(o))
171 |
172 | indextest = o |> Riak.Object.delete_all_indexes
173 | |> Riak.Object.get_all_indexes
174 |
175 | assert indextest == []
176 | end
177 |
178 | test "links", context do
179 | pid = context[:pid]
180 |
181 | o1 =Riak.Object.create(bucket: "user", key: "drew1", data: "Drew1 Kerrigan")
182 | Riak.put(pid, o1)
183 | o2 = Riak.Object.create(bucket: "user", key: "drew2", data: "Drew2 Kerrigan")
184 | Riak.put(pid, o2)
185 |
186 | key = Riak.Helper.random_key
187 |
188 | o = Riak.Object.create(bucket: "user", key: key, data: "Drew Kerrigan")
189 | |> Riak.Object.put_link("my_tag", "user", "drew1")
190 | |> Riak.Object.put_link("my_tag", "user", "drew2")
191 | Riak.put(pid, o)
192 |
193 | assert Riak.Object.get_link(o, "my_tag") == [{"user", "drew1"}, {"user", "drew2"}]
194 |
195 | assert Riak.Object.delete_link(o, "my_tag") |> Riak.Object.get_link("my_tag") == nil
196 |
197 | # Get the object again so we don't create a sibling
198 | o = Riak.find pid, "user", key
199 |
200 | o |> Riak.Object.put_link("my_tag", "user", "drew1")
201 | |> Riak.Object.put_link("my_tag", "user", "drew2")
202 | Riak.put(pid, o)
203 |
204 | assert Riak.Object.get_link(o, "my_tag") == [{"user", "drew1"}, {"user", "drew2"}]
205 |
206 | assert is_list(Riak.Object.get_all_links(o))
207 | assert Riak.Object.delete_all_links(o) |> Riak.Object.get_all_links == []
208 | end
209 |
210 | test "ping", context do
211 | assert Riak.ping(context[:pid]) == :pong
212 | end
213 |
214 | test "siblings", context do
215 | pid = context[:pid]
216 | assert :ok == Riak.Bucket.put pid, "user", [{:allow_mult, true}]
217 |
218 | key = Riak.Helper.random_key
219 |
220 | o1 = Riak.Object.create(bucket: "user", key: key, data: "Drew1 Kerrigan")
221 | Riak.put(pid, o1)
222 | o2 = Riak.Object.create(bucket: "user", key: key, data: "Drew2 Kerrigan")
223 | Riak.put(pid, o2)
224 |
225 | u = Riak.find pid, "user", key
226 |
227 | assert is_list(u)
228 |
229 | [h|_t] = u
230 |
231 | assert :ok == Riak.resolve(pid, "user", key, 2)
232 |
233 | u = Riak.find pid, "user", key
234 |
235 | assert u.data == h
236 |
237 | assert :ok == Riak.Bucket.reset pid, "user"
238 | end
239 | end
240 |
--------------------------------------------------------------------------------
/test/set_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Riak.CRDT.SetTest do
2 | use Riak.Case
3 | alias Riak.CRDT.Set
4 |
5 | @moduletag :riak2
6 |
7 | test "create, update and find a set" do
8 | key = Riak.Helper.random_key
9 |
10 | Set.new
11 | |> Set.put("foo")
12 | |> Set.put("bar")
13 | |> Riak.update("sets", "bucketset", key)
14 |
15 | set = Riak.find("sets", "bucketset", key)
16 | |> Set.value
17 |
18 | assert "foo" in set
19 | assert "bar" in set
20 | end
21 |
22 | test "size" do
23 | key = Riak.Helper.random_key
24 |
25 | Set.new
26 | |> Set.put("foo") |> Set.put("bar")
27 | |> Set.put("foo") |> Set.put("bar")
28 | |> Riak.update("sets", "bucketset", key)
29 |
30 | size = Riak.find("sets", "bucketset", key)
31 | |> Set.size
32 |
33 | assert size == 2
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/test/test_helper.exs:
--------------------------------------------------------------------------------
1 | ExUnit.configure(exclude: [skip: true])
2 | ExUnit.start
3 |
4 | defmodule Riak.Case do
5 | use ExUnit.CaseTemplate
6 |
7 | setup do
8 | {:ok, pid} = Riak.Connection.start('127.0.0.1', 8087)
9 | on_exit fn ->
10 | #Riak.Helper.clean! pid
11 | Process.exit(pid, :kill)
12 | end
13 | {:ok, pid: pid}
14 | end
15 | end
16 |
17 | defmodule Riak.Helper do
18 | def clean!(pid) do
19 | # This is terrible, and should not be used.
20 | for bucket <- Riak.Bucket.list!(pid), key <- Riak.Bucket.keys!(pid, bucket) do
21 | Riak.delete(pid, bucket, key)
22 | end
23 | end
24 |
25 | def random_key do
26 | {me, se, mi} = :erlang.timestamp
27 | "#{me}#{se}#{mi}"
28 | end
29 |
30 | # helper for chosing the index of a sibling value list
31 | def index_of(search, [search|_], index) do
32 | index
33 | end
34 | def index_of(search, [_|rest], index) do
35 | index_of(search, rest, index+1)
36 | end
37 | def index_of(search, haystack) do
38 | index_of(search, haystack, 1)
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/test/timeseries_test.exs:
--------------------------------------------------------------------------------
1 | defmodule Riak.TimeseriesTest do
2 | use Riak.Case
3 | alias Riak.Timeseries
4 |
5 | @moduletag :riakts
6 |
7 | test "Insert TS records and query them" do
8 | assert :ok == Timeseries.put("GeoCheckin", [
9 | {"region1", "state1", 25, "hot", 23.0},
10 | {"region2", "state99", 26, "windy", 19.0}
11 | ])
12 |
13 | e1 = {["region", "state", "time", "weather", "temperature"], [{"region1", "state1", 25, "hot", 23.0}]}
14 | r1 = Timeseries.query("select * from GeoCheckin where time > 24 and time < 26 and region = 'region1' and state = 'state1'")
15 |
16 | e2 = {["region", "state", "time", "weather", "temperature"],
17 | [{"region2", "state99", 26, "windy", 19.0}]}
18 | r2 = Timeseries.query("select * from GeoCheckin where time > 25 and time < 27 and region = 'region2' and state = 'state99'")
19 |
20 | r3_results = Timeseries.list!("GeoCheckin")
21 | r3 = contains_row(
22 | r3_results, {"region1", "state1", 25}) or contains_row(
23 | r3_results, {"region2", "state99", 26})
24 | e3 = true
25 |
26 | e4 = {["region", "state", "time", "weather", "temperature"],
27 | [{"region1", "state1", 25, "hot", 23.0}]}
28 | r4 = Timeseries.get("GeoCheckin", ["region1", "state1", 25])
29 |
30 | e5 = {["region", "state", "time", "weather", "temperature"],
31 | [{"region2", "state99", 26, "windy", 19.0}]}
32 | r5 = Timeseries.get("GeoCheckin", ["region2", "state99", 26])
33 |
34 | e6 = :ok
35 | r6 = Timeseries.delete("GeoCheckin", ["region1", "state1", 25])
36 |
37 | e7 = :ok
38 | r7 = Timeseries.delete("GeoCheckin", ["region2", "state99", 26])
39 |
40 | e8 = {[], []}
41 | r8 = Timeseries.get("GeoCheckin", ["region1", "state1", 25])
42 |
43 | e9 = {[], []}
44 | r9 = Timeseries.get("GeoCheckin", ["region2", "state99", 26])
45 |
46 | e10 = {[],[]}
47 | r10 = Timeseries.query("select * from GeoCheckin where time > 25 and time < 27 and region = 'region2' and state = 'state99'")
48 |
49 | assert e1 == r1
50 | assert e2 == r2
51 | assert e3 == r3
52 | assert e4 == r4
53 | assert e5 == r5
54 | assert e6 == r6
55 | assert e7 == r7
56 | assert e8 == r8
57 | assert e9 == r9
58 | assert e10 == r10
59 | end
60 |
61 | defp contains_row([_], _) do
62 | false
63 | end
64 | defp contains_row([row|_], row) do
65 | true
66 | end
67 | defp contains_row([_|rest], row) do
68 | contains_row(rest, row)
69 | end
70 | end
71 |
--------------------------------------------------------------------------------