riak-client 1.0.0.beta → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/.gitignore +7 -4
  2. data/Gemfile +12 -17
  3. data/Guardfile +1 -1
  4. data/LICENSE +16 -0
  5. data/README.markdown +178 -0
  6. data/RELEASE_NOTES.md +99 -0
  7. data/Rakefile +25 -1
  8. data/erl_src/riak_kv_test014_backend.beam +0 -0
  9. data/erl_src/riak_kv_test014_backend.erl +189 -0
  10. data/erl_src/riak_kv_test_backend.beam +0 -0
  11. data/erl_src/riak_kv_test_backend.erl +37 -19
  12. data/lib/riak/client.rb +322 -272
  13. data/lib/riak/client/beefcake_protobuffs_backend.rb +6 -10
  14. data/lib/riak/client/decaying.rb +28 -0
  15. data/lib/riak/client/excon_backend.rb +27 -11
  16. data/lib/riak/client/http_backend.rb +71 -2
  17. data/lib/riak/client/http_backend/configuration.rb +17 -3
  18. data/lib/riak/client/http_backend/transport_methods.rb +3 -3
  19. data/lib/riak/client/net_http_backend.rb +18 -14
  20. data/lib/riak/client/node.rb +111 -0
  21. data/lib/riak/client/pool.rb +180 -0
  22. data/lib/riak/client/protobuffs_backend.rb +15 -5
  23. data/lib/riak/client/search.rb +9 -3
  24. data/lib/riak/link.rb +5 -7
  25. data/lib/riak/locale/en.yml +1 -0
  26. data/lib/riak/node/configuration.rb +1 -0
  27. data/lib/riak/node/defaults.rb +19 -6
  28. data/lib/riak/node/generation.rb +9 -2
  29. data/lib/riak/node/log.rb +2 -2
  30. data/lib/riak/node/version.rb +22 -16
  31. data/lib/riak/robject.rb +19 -3
  32. data/lib/riak/serializers.rb +1 -1
  33. data/lib/riak/test_server.rb +10 -2
  34. data/lib/riak/version.rb +1 -1
  35. data/riak-client.gemspec +3 -3
  36. data/spec/failover/failover.rb +59 -0
  37. data/spec/integration/riak/http_backends_spec.rb +2 -2
  38. data/spec/integration/riak/node_spec.rb +16 -24
  39. data/spec/integration/riak/protobuffs_backends_spec.rb +1 -1
  40. data/spec/integration/riak/test_server_spec.rb +4 -3
  41. data/spec/integration/riak/threading_spec.rb +193 -0
  42. data/spec/riak/beefcake_protobuffs_backend/object_methods_spec.rb +23 -0
  43. data/spec/riak/beefcake_protobuffs_backend_spec.rb +4 -2
  44. data/spec/riak/bucket_spec.rb +2 -1
  45. data/spec/riak/client_spec.rb +80 -181
  46. data/spec/riak/excon_backend_spec.rb +3 -2
  47. data/spec/riak/http_backend/configuration_spec.rb +37 -5
  48. data/spec/riak/http_backend/object_methods_spec.rb +1 -1
  49. data/spec/riak/http_backend/transport_methods_spec.rb +2 -2
  50. data/spec/riak/http_backend_spec.rb +53 -3
  51. data/spec/riak/map_reduce_spec.rb +1 -1
  52. data/spec/riak/net_http_backend_spec.rb +1 -2
  53. data/spec/riak/node_spec.rb +173 -0
  54. data/spec/riak/pool_spec.rb +306 -0
  55. data/spec/riak/robject_spec.rb +8 -4
  56. data/spec/riak/search_spec.rb +66 -15
  57. data/spec/riak/serializers_spec.rb +12 -1
  58. data/spec/spec_helper.rb +9 -1
  59. data/spec/support/http_backend_implementation_examples.rb +6 -2
  60. data/spec/support/sometimes.rb +46 -0
  61. data/spec/support/test_server.rb +50 -19
  62. data/spec/support/unified_backend_examples.rb +11 -10
  63. data/spec/support/version_filter.rb +14 -0
  64. metadata +40 -29
  65. data/lib/active_support/cache/riak_store.rb +0 -2
  66. data/lib/riak/cache_store.rb +0 -84
  67. data/lib/riak/client/pump.rb +0 -30
  68. data/lib/riak/util/fiber1.8.rb +0 -48
  69. data/spec/integration/riak/cache_store_spec.rb +0 -129
data/.gitignore CHANGED
@@ -14,11 +14,12 @@ tmtags
14
14
  *.swp
15
15
 
16
16
  ## PROJECT::GENERAL
17
- coverage/**/*
18
- rdoc/**/*
19
- pkg/**/*
17
+ coverage
18
+ rdoc
19
+ pkg
20
20
 
21
21
  ## PROJECT::SPECIFIC
22
+ _notes
22
23
  doc
23
24
  .yardoc
24
25
  .bundle
@@ -28,5 +29,7 @@ Gemfile.lock
28
29
  *.rbc
29
30
  .rvmrc
30
31
 
31
- .ripplenode
32
32
  .riaktest
33
+ **/.riaktest
34
+ vendor/erlang
35
+ vendor/riak
data/Gemfile CHANGED
@@ -2,10 +2,13 @@ source :rubygems
2
2
 
3
3
  gemspec
4
4
  gem 'bundler'
5
- gem 'guard-rspec'
6
- gem 'rb-fsevent'
7
- gem 'growl'
8
5
 
6
+ group :guard do
7
+ gem 'guard-rspec'
8
+ gem 'rb-fsevent'
9
+ gem 'growl'
10
+ end
11
+
9
12
  platforms :mri do
10
13
  gem 'yajl-ruby'
11
14
  end
@@ -14,18 +17,10 @@ platforms :jruby do
14
17
  gem 'jruby-openssl'
15
18
  end
16
19
 
17
- group :integration do
18
- if ENV['RAILS31']
19
- gem 'activesupport', '~> 3.1.0'
20
- else
21
- gem 'activesupport', '~> 3.0.10'
22
- end
23
- end
20
+ # platforms :mri_18, :jruby do
21
+ # gem 'ruby-debug'
22
+ # end
24
23
 
25
- platforms :mri_18, :jruby do
26
- gem 'ruby-debug'
27
- end
28
-
29
- platforms :mri_19 do
30
- gem 'ruby-debug19'
31
- end
24
+ # platforms :mri_19 do
25
+ # gem 'ruby-debug19'
26
+ # end
data/Guardfile CHANGED
@@ -3,7 +3,7 @@
3
3
  gemset = ENV['RVM_GEMSET'] || 'ripple'
4
4
  gemset = "@#{gemset}" unless gemset.to_s == ''
5
5
 
6
- rvms = %w[ 1.8.7 1.9.2 jruby ].map do |version|
6
+ rvms = %w[ 1.8.7 1.9.2 1.9.3 ].map do |version|
7
7
  "#{version}@#{gemset}"
8
8
  end
9
9
 
data/LICENSE ADDED
@@ -0,0 +1,16 @@
1
+ Copyright 2010-2011 Sean Cribbs and Basho Technologies, Inc.
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.
data/README.markdown ADDED
@@ -0,0 +1,178 @@
1
+ # Riak Ruby Client (riak-client) [![Build Status](https://secure.travis-ci.org/basho/riak-ruby-client.png)](http://travis-ci.org/basho/riak-ruby-client)
2
+
3
+ `riak-client` is a rich Ruby client/toolkit for Riak, Basho's
4
+ distributed database that contains a basic wrapper around typical
5
+ operations, including bucket manipulation, object CRUD, link-walking,
6
+ and map-reduce.
7
+
8
+ ## Dependencies
9
+
10
+ `riak-client` requires i18n, builder, beefcake, and multi_json. For
11
+ higher performance on HTTP requests, install the 'excon' gem. The
12
+ cache store implementation requires ActiveSupport 3 or later.
13
+
14
+ Development dependencies are handled with bundler. Install bundler
15
+ (`gem install bundler`) and run this command in each sub-project to
16
+ get started:
17
+
18
+ ``` bash
19
+ $ bundle install
20
+ ```
21
+
22
+ Run the RSpec suite using `bundle exec`:
23
+
24
+ ``` bash
25
+ $ bundle exec rake
26
+ ```
27
+
28
+ ## Basic Example
29
+
30
+ ``` ruby
31
+ require 'riak'
32
+
33
+ # Create a client interface
34
+ client = Riak::Client.new
35
+
36
+ # Create a client interface that uses Excon
37
+ client = Riak::Client.new(:http_backend => :Excon)
38
+
39
+ # Create a client that uses Protocol Buffers
40
+ client = Riak::Client.new(:protocol => "pbc")
41
+
42
+ # Automatically balance between multiple nodes
43
+ client = Riak::Client.new(:nodes => [
44
+ {:host => '10.0.0.1'},
45
+ {:host => '10.0.0.2', :pb_port => 1234},
46
+ {:host => '10.0.0.3', :http_port => 5678}
47
+ ])
48
+
49
+ # Retrieve a bucket
50
+ bucket = client.bucket("doc") # a Riak::Bucket
51
+
52
+ # Get an object from the bucket
53
+ object = bucket.get_or_new("index.html") # a Riak::RObject
54
+
55
+ # Change the object's data and save
56
+ object.data = "<html><body>Hello, world!</body></html>"
57
+ object.content_type = "text/html"
58
+ object.store
59
+
60
+ # Reload an object you already have
61
+ object.reload # Works if you have the key and vclock, using conditional GET
62
+ object.reload :force => true # Reloads whether you have the vclock or not
63
+
64
+ # Access more like a hash, client[bucket][key]
65
+ client['doc']['index.html'] # the Riak::RObject
66
+
67
+ # Create a new object
68
+ new_one = Riak::RObject.new(bucket, "application.js")
69
+ new_one.content_type = "application/javascript" # You must set the content type.
70
+ new_one.data = "alert('Hello, World!')"
71
+ new_one.store
72
+ ```
73
+
74
+ ## Map-Reduce Example
75
+
76
+ ``` ruby
77
+ # Assuming you've already instantiated a client, get the album titles for The Beatles
78
+ results = Riak::MapReduce.new(client).
79
+ add("artists","Beatles").
80
+ link(:bucket => "albums").
81
+ map("function(v){ return [JSON.parse(v.values[0].data).title]; }", :keep => true).run
82
+
83
+ p results # => ["Please Please Me", "With The Beatles", "A Hard Day's Night",
84
+ # "Beatles For Sale", "Help!", "Rubber Soul",
85
+ # "Revolver", "Sgt. Pepper's Lonely Hearts Club Band", "Magical Mystery Tour",
86
+ # "The Beatles", "Yellow Submarine", "Abbey Road", "Let It Be"]
87
+ ```
88
+
89
+
90
+ ## Riak Search Examples
91
+
92
+ For more information about Riak Search, see [the Basho wiki](http://wiki.basho.com/Riak-Search.html).
93
+
94
+ ``` ruby
95
+ # Create a client, specifying the Solr-compatible endpoint
96
+ # When connecting to Riak 0.14 and later, the Solr endpoint configuration option is not necessary.
97
+ client = Riak::Client.new :solr => "/solr"
98
+
99
+ # Search the default index for documents
100
+ result = client.search("title:Yesterday") # Returns a vivified JSON object
101
+ # containing 'responseHeaders' and 'response' keys
102
+ result['response']['numFound'] # total number of results
103
+ result['response']['start'] # offset into the total result set
104
+ result['response']['docs'] # the list of indexed documents
105
+
106
+ # Search the 'users' index for documents
107
+ client.search("users", "name:Sean")
108
+
109
+ # Add a document to an index
110
+ client.index("users", {:id => "sean@basho.com", :name => "Sean Cribbs"}) # adds to the 'users' index
111
+
112
+ client.index({:id => "index.html", :content => "Hello, world!"}) # adds to the default index
113
+
114
+ client.index({:id => 1, :name => "one"}, {:id => 2, :name => "two"}) # adds multiple docs
115
+
116
+ # Remove document(s) from an index
117
+ client.remove({:id => 1}) # removes the document with ID 1
118
+ client.remove({:query => "archived"}) # removes all documents matching query
119
+ client.remove({:id => 1}, {:id => 5}) # removes multiple docs
120
+
121
+ client.remove("users", {:id => "sean@basho.com"}) # removes from the 'users' index
122
+
123
+ # Seed MapReduce with search results
124
+ Riak::MapReduce.new(client).
125
+ search("users","email:basho").
126
+ map("Riak.mapValuesJson", :keep => true).
127
+ run
128
+
129
+ # Detect whether a bucket has auto-indexing
130
+ client['users'].is_indexed?
131
+
132
+ # Enable auto-indexing on a bucket
133
+ client['users'].enable_index!
134
+
135
+ # Disable auto-indexing on a bucket
136
+ client['users'].disable_index!
137
+ ```
138
+
139
+ ## How to Contribute
140
+
141
+ * Fork the project on [Github](http://github.com/basho/riak-ruby-client). If you have already forked, use `git pull --rebase` to reapply your changes on top of the mainline. Example:
142
+
143
+ ``` bash
144
+ $ git checkout master
145
+ $ git pull --rebase basho master
146
+ ```
147
+ * Create a topic branch. If you've already created a topic branch, rebase it on top of changes from the mainline "master" branch. Examples:
148
+ * New branch:
149
+
150
+ ``` bash
151
+ $ git checkout -b topic
152
+ ```
153
+ * Existing branch:
154
+
155
+ ``` bash
156
+ $ git rebase master
157
+ ```
158
+ * Write an RSpec example or set of examples that demonstrate the necessity and validity of your changes. **Patches without specs will most often be ignored. Just do it, you'll thank me later.** Documentation patches need no specs, of course.
159
+ * Make your feature addition or bug fix. Make your specs and stories pass (green).
160
+ * Run the suite using multiruby or rvm to ensure cross-version compatibility.
161
+ * Cleanup any trailing whitespace in your code (try @whitespace-mode@ in Emacs, or "Remove Trailing Spaces in Document" in the "Text" bundle in Textmate). You can use the `clean_whitespace` Rake task if you like.
162
+ * Commit, do not mess with Rakefile. If related to an existing issue in the [tracker](http://github.com/basho/ruby-riak-client/issues), include "Closes #X" in the commit message (where X is the issue number).
163
+ * Send a pull request to the Basho repository.
164
+
165
+ ## License & Copyright
166
+
167
+ Copyright &copy;2010-2012 Sean Cribbs and Basho Technologies, Inc.
168
+
169
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
170
+
171
+ [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)
172
+
173
+ Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
174
+
175
+ ## Auxillary Licenses
176
+
177
+ The included photo (spec/fixtures/cat.jpg) is Copyright &copy;2009 [Sean Cribbs](http://seancribbs.com/), and is licensed under the [Creative Commons Attribution Non-Commercial 3.0](http://creativecommons.org/licenses/by-nc/3.0) license.
178
+ !["Creative Commons"](http://i.creativecommons.org/l/by-nc/3.0/88x31.png)
data/RELEASE_NOTES.md ADDED
@@ -0,0 +1,99 @@
1
+ # Riak Ruby Client Release Notes
2
+
3
+ ## 1.0.0 Feature Release - 2012-02-03
4
+
5
+ Release 1.0.0 is a major feature release and is the first where
6
+ `riak-client`, `ripple`, and `riak-sessions` will be released
7
+ independently (see below). Because there too many individual changes
8
+ to recount, this entry will cover the major features and bugfixes
9
+ present in the release.
10
+
11
+ ### Riak 1.0/1.1 Compatibility
12
+
13
+ `riak-client` is fully compatible with Riak 1.0.x and
14
+ (yet-to-be-released) 1.1.x, including supporting secondary indexes,
15
+ integrated search, and cluster membership commands.
16
+
17
+ ### Multi-node Connections and Retries
18
+
19
+ `Riak::Client` can now connect to multiple Riak nodes at once. This
20
+ greatly improves throughput and allows the client to recover from
21
+ intermittent connection errors while continuing normal operation. To
22
+ enable this, all uses of the Pump/Fiber logic were removed in favor of
23
+ connection pools from which any new request can draw an existing or
24
+ create a new connection. Which node is selected for any new connection
25
+ is based on a quickly-decaying EWMA of its success rate on recent
26
+ requests. A huge thanks to [Kyle Kingsbury](https://github.com/aphyr)
27
+ who did most of the work on this!
28
+
29
+ ### Improved TestServer and Node Generation
30
+
31
+ The `Riak::TestServer` class has been generalized such that you can
32
+ generate regular nodes and even clusters that store data on disk. This
33
+ is especially useful if you want separate nodes or clusters for each
34
+ project that uses Riak, and to keep them separate from your base
35
+ install. `TestServer` also now launches the node in a separate process
36
+ (not a child process) so you can keep it running between test
37
+ suites. Clearing the in-memory data is performed by connecting to the
38
+ console via the exposed Unix pipes, rather than over stdio.
39
+
40
+ ### Conflict Resolution
41
+
42
+ An important part of dealing with eventual consistency is the ability
43
+ to handle when conflicts (also called siblings) are created. Now you
44
+ can resolve them automatically by registering blocks (callbacks) using
45
+ `Riak::RObject.on_conflict`. The block will be called when fetching a
46
+ key in conflict and receives a `RObject` that has siblings. To resolve
47
+ the conflict, it simply returns the resolved object, or nil if it
48
+ didn't handle the conflict. A huge thanks to
49
+ [Myron Marston](https://github.com/myronmarston) who implemented this!
50
+
51
+ ### Serializers
52
+
53
+ Before, serialization of Ruby objects into Riak was constrained to
54
+ three formats: JSON, YAML and Marshal. Now you can define your own
55
+ serializers so that you can store data in BSON, MsgPack, NetStrings,
56
+ or whatever format you like. Use `Riak::Serializers[content_type] =
57
+ serializer` to assign a serializer for the selected media type. The
58
+ serializer must respond to `#dump` and `#load`. (More handiwork of Myron
59
+ Marston, thanks!)
60
+
61
+ ### Stamps
62
+
63
+ If you don't like the keys that Riak hands out when you store an
64
+ `RObject` without a key, and you want something naturally ordered, you
65
+ can now generate them client-side using `Riak::Stamp`, which will
66
+ generate 64-bit integers in a fashion similar to Twitter's Snowflake,
67
+ but uses `Riak::Client#client_id` as the machine identifier.
68
+
69
+ ### Repository/Feature split
70
+
71
+ In an effort to decouple development of the individual projects and
72
+ reduce top-level dependencies, the `ripple` repository was split into
73
+ new repositories containing its corresponding sub-projects.
74
+ Additionally, the `Riak::CacheStore` has become its own project/gem.
75
+ The new gem and repository locations are below:
76
+
77
+ * [`riak-client`](http://rubygems.org/gems/riak-client) &mdash;
78
+ [basho/riak-ruby-client](https://github.com/basho/riak-ruby-client)
79
+ * [`ripple`](http://rubygems.org/gems/ripple) &mdash;
80
+ [seancribbs/ripple](https://github.com/seancribbs/ripple)
81
+ * [`riak-sessions`](http://rubygems.org/gems/riak-sessions) &mdash;
82
+ [seancribbs/riak-sessions](https://github.com/seancribbs/riak-sessions)
83
+ * [`riak-cache`](http://rubygems.org/gems/riak-cache) &mdash;
84
+ [seancribbs/riak-cache](https://github.com/seancribbs/riak-cache)
85
+
86
+ ### Significant Known Issues
87
+
88
+ Attempting to use the Protocol Buffers transport with a 0.14.x cluster
89
+ may cause the connection to dump because of incompatibilities in
90
+ certain protocol messages. This will be addressed in a future
91
+ patch/bugfix release.
92
+
93
+ The new node generation and test server intermittently fails on JRuby,
94
+ specifically from deadlocks related to blocking opens for the console
95
+ FIFOs. The JRuby team has helped on this issue, but there may not be a
96
+ clear resolution path until JRuby 1.7 or later.
97
+
98
+ Other known issues may be found on the
99
+ [Github issue tracker](https://github.com/basho/riak-ruby-client/issues?milestone=1).
data/Rakefile CHANGED
@@ -24,9 +24,28 @@ task :release => :gem do
24
24
  system "gem push pkg/#{gemspec.name}-#{gemspec.version}.gem"
25
25
  end
26
26
 
27
+ desc "Cleans up white space in source files"
28
+ task :clean_whitespace do
29
+ no_file_cleaned = true
30
+
31
+ Dir["**/*.rb"].each do |file|
32
+ contents = File.read(file)
33
+ cleaned_contents = contents.gsub(/([ \t]+)$/, '')
34
+ unless cleaned_contents == contents
35
+ no_file_cleaned = false
36
+ puts " - Cleaned #{file}"
37
+ File.open(file, 'w') { |f| f.write(cleaned_contents) }
38
+ end
39
+ end
40
+
41
+ if no_file_cleaned
42
+ puts "No files with trailing whitespace found"
43
+ end
44
+ end
45
+
27
46
  desc "Run Unit Specs Only"
28
47
  RSpec::Core::RakeTask.new(:spec) do |spec|
29
- spec.rspec_opts = %w[--profile --tag ~integration]
48
+ spec.rspec_opts = %w[--profile --tag ~integration --tag ~slow]
30
49
  end
31
50
 
32
51
  namespace :spec do
@@ -39,6 +58,11 @@ namespace :spec do
39
58
  RSpec::Core::RakeTask.new(:all) do |spec|
40
59
  spec.rspec_opts = %w[--profile --tag '~slow']
41
60
  end
61
+
62
+ desc "Run Slow Specs Only"
63
+ RSpec::Core::RakeTask.new(:slow) do |spec|
64
+ spec.rspec_opts = %w[--profile --tag slow]
65
+ end
42
66
  end
43
67
 
44
68
  desc "Run All Specs (including slow specs)"
@@ -0,0 +1,189 @@
1
+ %% -------------------------------------------------------------------
2
+ %%
3
+ %% riak_kv_test_backend: storage engine based on ETS tables
4
+ %%
5
+ %% Copyright (c) 2007-2010 Basho Technologies, Inc. All Rights Reserved.
6
+ %%
7
+ %% This file is provided to you under the Apache License,
8
+ %% Version 2.0 (the "License"); you may not use this file
9
+ %% except in compliance with the License. You may obtain
10
+ %% a copy of the License at
11
+ %%
12
+ %% http://www.apache.org/licenses/LICENSE-2.0
13
+ %%
14
+ %% Unless required by applicable law or agreed to in writing,
15
+ %% software distributed under the License is distributed on an
16
+ %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
+ %% KIND, either express or implied. See the License for the
18
+ %% specific language governing permissions and limitations
19
+ %% under the License.
20
+ %%
21
+ %% -------------------------------------------------------------------
22
+
23
+ % @doc riak_kv_test_backend is a Riak storage backend using ets that
24
+ % exposes a reset function for efficiently clearing stored data.
25
+
26
+ -module(riak_kv_test014_backend).
27
+ -behavior(riak_kv_backend).
28
+ -behavior(gen_server).
29
+ -ifdef(TEST).
30
+ -include_lib("eunit/include/eunit.hrl").
31
+ -endif.
32
+ -export([start/2,stop/1,get/2,put/3,list/1,list_bucket/2,delete/2,
33
+ is_empty/1, drop/1, fold/3, callback/3, reset/0]).
34
+
35
+ -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
36
+ terminate/2, code_change/3]).
37
+
38
+
39
+ % @type state() = term().
40
+ -record(state, {t, p}).
41
+
42
+ % @spec start(Partition :: integer(), Config :: proplist()) ->
43
+ % {ok, state()} | {{error, Reason :: term()}, state()}
44
+ start(Partition, _Config) ->
45
+ gen_server:start_link(?MODULE, [Partition], []).
46
+
47
+ % @spec reset() -> ok | {error, timeout}
48
+ reset() ->
49
+ Pids = lists:foldl(fun(Item, Acc) ->
50
+ case lists:prefix("test_backend", atom_to_list(Item)) of
51
+ true -> [whereis(Item)|Acc];
52
+ _ -> Acc
53
+ end
54
+ end, [], registered()),
55
+ [gen_server:cast(Pid,{reset, self()})|| Pid <- Pids],
56
+ receive_reset(Pids).
57
+
58
+ receive_reset([]) -> ok;
59
+ receive_reset(Pids) ->
60
+ receive
61
+ {reset, Pid} ->
62
+ receive_reset(lists:delete(Pid, Pids))
63
+ after 1000 ->
64
+ {error, timeout}
65
+ end.
66
+
67
+ %% @private
68
+ init([Partition]) ->
69
+ PName = list_to_atom("test_backend" ++ integer_to_list(Partition)),
70
+ P = list_to_atom(integer_to_list(Partition)),
71
+ register(PName, self()),
72
+ {ok, #state{t=ets:new(P,[]), p=P}}.
73
+
74
+ %% @private
75
+ handle_cast({reset,From}, State) ->
76
+ ets:delete_all_objects(State#state.t),
77
+ From ! {reset, self()},
78
+ {noreply, State};
79
+ handle_cast(_, State) -> {noreply, State}.
80
+
81
+ %% @private
82
+ handle_call(stop,_From,State) -> {reply, srv_stop(State), State};
83
+ handle_call({get,BKey},_From,State) -> {reply, srv_get(State,BKey), State};
84
+ handle_call({put,BKey,Val},_From,State) ->
85
+ {reply, srv_put(State,BKey,Val),State};
86
+ handle_call({delete,BKey},_From,State) -> {reply, srv_delete(State,BKey),State};
87
+ handle_call(list,_From,State) -> {reply, srv_list(State), State};
88
+ handle_call({list_bucket,Bucket},_From,State) ->
89
+ {reply, srv_list_bucket(State, Bucket), State};
90
+ handle_call(is_empty, _From, State) ->
91
+ {reply, ets:info(State#state.t, size) =:= 0, State};
92
+ handle_call(drop, _From, State) ->
93
+ ets:delete(State#state.t),
94
+ {reply, ok, State};
95
+ handle_call({fold, Fun0, Acc}, _From, State) ->
96
+ Fun = fun({{B,K}, V}, AccIn) -> Fun0({B,K}, V, AccIn) end,
97
+ Reply = ets:foldl(Fun, Acc, State#state.t),
98
+ {reply, Reply, State}.
99
+
100
+ % @spec stop(state()) -> ok | {error, Reason :: term()}
101
+ stop(SrvRef) -> gen_server:call(SrvRef,stop).
102
+ srv_stop(State) ->
103
+ true = ets:delete(State#state.t),
104
+ ok.
105
+
106
+ % get(state(), riak_object:bkey()) ->
107
+ % {ok, Val :: binary()} | {error, Reason :: term()}
108
+ % key must be 160b
109
+ get(SrvRef, BKey) -> gen_server:call(SrvRef,{get,BKey}).
110
+ srv_get(State, BKey) ->
111
+ case ets:lookup(State#state.t,BKey) of
112
+ [] -> {error, notfound};
113
+ [{BKey,Val}] -> {ok, Val};
114
+ Err -> {error, Err}
115
+ end.
116
+
117
+ % put(state(), riak_object:bkey(), Val :: binary()) ->
118
+ % ok | {error, Reason :: term()}
119
+ % key must be 160b
120
+ put(SrvRef, BKey, Val) -> gen_server:call(SrvRef,{put,BKey,Val}).
121
+ srv_put(State,BKey,Val) ->
122
+ true = ets:insert(State#state.t, {BKey,Val}),
123
+ ok.
124
+
125
+ % delete(state(), riak_object:bkey()) ->
126
+ % ok | {error, Reason :: term()}
127
+ % key must be 160b
128
+ delete(SrvRef, BKey) -> gen_server:call(SrvRef,{delete,BKey}).
129
+ srv_delete(State, BKey) ->
130
+ true = ets:delete(State#state.t, BKey),
131
+ ok.
132
+
133
+ % list(state()) -> [riak_object:bkey()]
134
+ list(SrvRef) -> gen_server:call(SrvRef,list).
135
+ srv_list(State) ->
136
+ MList = ets:match(State#state.t,{'$1','_'}),
137
+ list(MList,[]).
138
+ list([],Acc) -> Acc;
139
+ list([[K]|Rest],Acc) -> list(Rest,[K|Acc]).
140
+
141
+ % list_bucket(term(), Bucket :: riak_object:bucket()) -> [Key :: binary()]
142
+ list_bucket(SrvRef, Bucket) ->
143
+ gen_server:call(SrvRef,{list_bucket, Bucket}).
144
+ srv_list_bucket(State, {filter, Bucket, Fun}) ->
145
+ MList = lists:filter(Fun, ets:match(State#state.t,{{Bucket,'$1'},'_'})),
146
+ list(MList,[]);
147
+ srv_list_bucket(State, Bucket) ->
148
+ case Bucket of
149
+ '_' -> MatchSpec = {{'$1','_'},'_'};
150
+ _ -> MatchSpec = {{Bucket,'$1'},'_'}
151
+ end,
152
+ MList = ets:match(State#state.t,MatchSpec),
153
+ list(MList,[]).
154
+
155
+ is_empty(SrvRef) -> gen_server:call(SrvRef, is_empty).
156
+
157
+ drop(SrvRef) -> gen_server:call(SrvRef, drop).
158
+
159
+ fold(SrvRef, Fun, Acc0) -> gen_server:call(SrvRef, {fold, Fun, Acc0}, infinity).
160
+
161
+ %% Ignore callbacks for other backends so multi backend works
162
+ callback(_State, _Ref, _Msg) ->
163
+ ok.
164
+
165
+ %% @private
166
+ handle_info(_Msg, State) -> {noreply, State}.
167
+
168
+ %% @private
169
+ terminate(_Reason, _State) -> ok.
170
+
171
+ %% @private
172
+ code_change(_OldVsn, State, _Extra) -> {ok, State}.
173
+
174
+ %%
175
+ %% Test
176
+ %%
177
+ -ifdef(TEST).
178
+
179
+ % @private
180
+ simple_test() ->
181
+ riak_kv_backend:standard_test(?MODULE, []).
182
+
183
+ -ifdef(EQC).
184
+ %% @private
185
+ eqc_test() ->
186
+ ?assertEqual(true, backend_eqc:test(?MODULE, true)).
187
+
188
+ -endif. % EQC
189
+ -endif. % TEST