Giter Club home page Giter Club logo

riak-ruby-client's Introduction

Riak Ruby Client (riak-client)

riak-client is a rich Ruby client/toolkit for Riak, Basho's distributed database that contains a basic wrapper around typical operations, including bucket manipulation, object CRUD, link-walking, and map-reduce.

Exhaustive documentation is available at http://basho.github.io/riak-ruby-client/ , and API documentation can be read at http://www.rubydoc.info/gems/riak-client/frames .

Build Status

Build Status

Dependencies

Ruby 1.9.3, 2.0, 2.1, and 2.2 are supported. JRuby in 2.0 mode is also supported.

Travis CI is configured to run unit tests on 1.9.3, 2.X versions of Ruby and JRuby.

NOTE: official support for the Ruby 1.9 series ended February of 2015.

In JRuby 1.7.13, OCSP validation is absent, and CRL validation always fails. This issue is being tracked and this document will be updated when it is fixed. Additionally, client certificate authentication doesn't work in JRuby. This issue is also being tracked, and this document will be updated when it works.

riak-client requires beefcake, cert_validator, i18n, innertube, and multi_json.

Development dependencies are handled with bundler. Install bundler (gem install bundler) and run this command to get started:

$ bundle install

Run the RSpec suite using bundle exec:

$ bundle exec rake

Basic Example

require 'riak'

# Create a client interface
client = Riak::Client.new

# Create a client that uses secure Protocol Buffers
client = Riak::Client.new(authentication: {
      # certificate authority to validate the server cert
      ca_file: '/home/zedo/ca.crt',

      # username, required
      user: 'zedo',

      # password for password-based authentication
      password: 'catnip',

      # client-cert authentication parameters support filenames,
      # OpenSSL-compatible string data, or properly initialized
      # OpenSSL objects
      client_ca: '/home/zedo/ca.crt',
      cert: File.read '/home/zedo/zedo.crt',
      key: OpenSSL::PKey::RSA.new(File.read '/home/zedo/zedo.key')
    })

# Automatically balance between multiple nodes
client = Riak::Client.new(:nodes => [
  {:host => '10.0.0.1'},
  {:host => '10.0.0.2', :pb_port => 1234},
  {:host => '10.0.0.3', :pb_port => 5678}
])

# Retrieve a bucket
bucket = client.bucket("doc")  # a Riak::Bucket

# Get an object from the bucket
object = bucket.get_or_new("index.html")   # a Riak::RObject

# Change the object's data and save
object.raw_data = "<html><body>Hello, world!</body></html>"
object.content_type = "text/html"
object.store

# Reload an object you already have
object.reload                  # Works if you have the key and vclock, using conditional GET
object.reload :force => true   # Reloads whether you have the vclock or not

# Access more like a hash, client[bucket][key]
client['doc']['index.html']   # the Riak::RObject

# Create a new object
new_one = Riak::RObject.new(bucket, "application.js")
new_one.content_type = "application/javascript" # You must set the content type.
new_one.raw_data = "alert('Hello, World!')"
new_one.store

Bucket Types

Riak 2 uses bucket types to enable groups of similar buckets to share properties, configuration, and to namespace values within those buckets. Bucket type support is integral to how CRDTs work.

In versions 2.2.0 and newer of this client, bucket types have a first-class representation, and can be used to create BucketTyped::Bucket objects that are namespaced differently from regular Riak::Bucket objects.

# This example assumes you have a "beverages" bucket type.
beverages = client.bucket_type 'beverages'

coffees = beverages.bucket 'coffees'
untyped_coffees = client.bucket 'coffees'

chapadao = coffees.new 'chapadao'
chapadao.data = "Chapadao de Ferro"
chapadao.store # stores this in the "beverages" bucket type

untyped_coffees.get 'chapadao' # raises error, not found
coffees.get 'chapadao' # succeeds

chapadao.reload # succeeds

untyped_coffees.delete 'chapadao' # silently fails to delete it
chapadao.delete # deletes it
coffees.delete 'chapadao' # deletes it

Client 2.0 and 2.1 code that uses the type argument to methods still works:

coffees = client.bucket 'coffees'

chapadao = coffees.new 'chapadao'
chapadao.data = "Chapadao de Ferro"
chapadao.store type: 'beverages' # stores this in the "beverages" bucket type

coffees.get 'chapadao' # raises error, not found
coffees.get 'chapadao', type: 'beverages' # succeeds

chapadao.reload # raises error, not found
chapadao.reload type: 'beverages' # succeeds

chapadao.delete # silently fails to delete it
coffees.delete 'chapadao' # silently fails to delete it

chapadao.delete type: 'beverages' # deletes it
coffees.delete 'chapadao', type: 'beverages' # deletes it

Map-Reduce Example

# Assuming you've already instantiated a client, get the album titles for The Beatles
results = Riak::MapReduce.new(client).
                add("artists","Beatles").
                link(:bucket => "albums").
                map("function(v){ return [JSON.parse(v.values[0].data).title]; }", :keep => true).run

p results # => ["Please Please Me", "With The Beatles", "A Hard Day's Night",
          #     "Beatles For Sale", "Help!", "Rubber Soul",
          #     "Revolver", "Sgt. Pepper's Lonely Hearts Club Band", "Magical Mystery Tour",
          #     "The Beatles", "Yellow Submarine", "Abbey Road", "Let It Be"]

Riak Search Examples

This client supports the new Riak Search 2 (codenamed "Yokozuna"). For more information about Riak Search, see the Riak documentation.

This documentation assumes there's a yokozuna bucket type created and activated.

# Create a client and bucket.
client = Riak::Client.new
bucket_type = client.bucket_type 'yokozuna'
bucket = bucket_type.bucket 'pizzas'

# Create an index and add it to a typed bucket. Setting the index on the bucket
# may fail until the index creation has propagated.
index = Riak::Search::Index.new client, 'pizzas'
index.exist? #=> false
index.create!
client.set_bucket_props bucket, {search_index: 'pizzas'}, 'yokozuna'

# Store some records for indexing
meat = bucket.new 'meat'
meat.data = {toppings_ss: %w{pepperoni ham sausage}}
meat.store type: 'yokozuna'

hawaiian = bucket.new 'hawaiian'
hawaiian.data = {toppings_ss: %w{ham pineapple}}
hawaiian.store type: 'yokozuna'

# Search the pizzas index for hashes that have a "ham" entry in the toppings_ss array
query = Riak::Search::Query.new client, index, 'toppings_ss:ham'
query.rows = 5 # return the first five pizzas

result = query.results # returns a ResultsCollection object
result.length # number of results returned
result.num_found # total number of results found, including ones not returned

pizza_result = result.first # a ResultDocument of the first pizza
pizza_result.score # score of the match
pizza_result.key # also pizza.bucket and pizza.bucket_type

pizza = pizza_result.robject # load the actual RObject for the match

Secondary Index Examples

Riak supports secondary indexes. Secondary indexing, or "2i," gives you the ability to tag objects with multiple queryable values at write time, and then query them later.

Tagging Objects

Objects are tagged with a hash kept behind the indexes method. Secondary index storage logic is in lib/riak/rcontent.rb.

object = bucket.get_or_new 'cobb.salad'

# Indexes end with the "_bin" suffix to indicate they're binary or string
# indexes. They can have multiple values.
object.indexes['ingredients_bin'] = %w{lettuce tomato bacon egg chives}

# Indexes ending with the "_int" suffix are indexed as integers. They can
# have multiple values too.
object.indexes['calories_int'] = [220]

# You must re-save the object to store indexes.
object.store

Finding Objects

Secondary index queries return a list of keys exactly matching a scalar or within a range.

# The Bucket#get_index method allows querying by scalar...
bucket.get_index 'calories_int', 220 # => ['cobb.salad']

# or range.
bucket.get_index 'calories_int', 100..300 # => ['cobb.salad']

# Binary indexes also support both ranges and scalars.
bucket.get_index 'ingredients_bin', 'tomata'..'tomatz' # => ['cobb.salad']

# The collection from #get_index also provides a continuation for pagination:
c = bucket.get_index 'ingredients_bin', 'lettuce', max_results: 5
c.length # => 5
c.continuation # => "g2gCbQAAA="

# You can use that continuation to get the next page of results:
c2 = bucket.get_index 'ingredients_bin', 'lettuce', max_results: 5, continuation: c.continuation

# More complicated operations may benefit by using the `SecondaryIndex` object:
q = Riak::SecondaryIndex.new bucket, 'ingredients_bin', 'lettuce', max_results: 5

# SecondaryIndex objects give you access to the keys...
q.keys # => ['cobb.salad', 'wedge.salad', 'buffalo_chicken.wrap', ...]

# but can also fetch values for you in parallel.
q.values # => [<RObject {recipes,cobb.salad} ...>, <RObject {recipes,wedge...

# They also provide simpler pagination:
q.has_next_page? # => true
q2 = q.next_page

Riak 2 Data Types

Riak 2 features new distributed data structures: counters, sets, and maps (containing counters, flags, maps, registers, and sets). These are implemented by the Riak database as Convergent Replicated Data Types.

Riak data type support requires bucket types to be configured to support each top-level data type. If you're just playing around, use the Riak Ruby Vagrant setup to get started with the appropriate configuration and bucket types quickly.

The examples below presume that the appropriate bucket types are named counters, maps, and sets; these bucket type names are the client's defaults. Viewing and changing the defaults is easy:

Riak::Crdt::DEFAULT_BUCKET_TYPES[:set] #=> "sets"

Riak::Crdt::DEFAULT_BUCKET_TYPES[:set] = "a_cooler_set"

The top-level CRDT types have both immediate and batch mode. If you're doing multiple writes to a single top-level counter or set, or updating multiple map entries, batch mode will make fewer round-trips to Riak.

Top-level CRDT types accept nil as a key. This allows Riak to assign a random key for them.

Deleting CRDTs requires you to use the key-value API for the time being.

brews = Riak::Crdt::Set.new bucket, 'brews'
brews.add 'espresso'
brews.add 'aeropress'

bucket.delete brews.key, type: brews.bucket_type

Counters

Riak 2 integer counters have one operation: increment by an integer.

counter = Riak::Crdt::Counter.new bucket, key

counter.value #=> 15

counter.increment

counter.value #=> 16

counter.increment 3

counter.value #=> 19

counter.decrement

counter.value #=> 18

Counter operations can be batched:

counter.batch do |c|
  c.increment
  c.increment 5
end

Maps

Riak 2 maps can contain counters, flags (booleans), registers (strings), sets, and other maps.

Maps are similar but distinct from the standard Ruby Hash. Entries are segregated by both name and type, so you can have counters, registers, and sets inside a map that all have the same name.

map = Riak::Crdt::Map.new bucket, key

map.counters['potatoes'].value #=> 5
map.sets['potatoes'].include? 'yukon gold' #=> true

map.sets['cacti'].value #=> #<Set: {"saguaro", "prickly pear", "fishhook"}>
map.sets['cacti'].remove 'prickly pear'

map.registers['favorite butterfly'] = 'the mighty monarch'

map.flags['pet cat'] = true

map.maps['atlantis'].registers['location'] #=> 'kennedy space center'

map.counters.delete 'thermometers'

Maps are a prime candidate for batched operations:

map.batch do |m|
  m.counters['hits'].increment
  m.sets['followers'].add 'basho_elevator'
end

Frequently, you might want a map with a Riak-assigned name instead of one you come up with yourself:

map = Riak::Crdt::Map.new bucket, nil

map.registers['coat_pattern'] = 'tabby'

map.key #=> "2do4NvcurWhXYNQg8HoIR9zedJV"

Sets

Sets are an unordered collection of entries.

PROTIP: Ruby and Riak Ruby Client both have classes called Set. Be careful to refer to the Ruby version as ::Set and the Riak client version as Riak::Crdt::Set.

set = Riak::Crdt::Set.new bucket, key

set.members #=> #<Set: {"Edinburgh", "Leeds", "London"}>

set.add "Newcastle"
set.remove "London"

set.include? "Leeds" #=> true

Sets support batched operations:

set.batch do |s|
  s.add "York"
  s.add "Aberdeen"
  s.remove "Newcastle"
end

Client Implementation Notes

The client code for these types is in the Riak::Crdt namespace, and mostly in the lib/riak/crdt directory.

Riak 1.4 Counters

For more information about 1.4-style counters in Riak, see the Basho documentation.

Counter records are automatically persisted on increment or decrement. The initial default value is 0.

# Firstly, ensure that your bucket is allow_mult set to true
bucket = client.bucket "counters"
bucket.allow_mult = true

# You can create a counter by using the bucket's counter method
counter = bucket.counter("counter-key-here")
counter.increment
=> nil

p counter.value
1
=> 1

# Let's increment one more time and then retrieve it from the database
counter.increment

# Retrieval is similar to creation
persisted_counter = Riak::Counter.new(bucket, "counter-key-here")

p persisted_counter.value
2
=> 2

# We can also increment by a specified number
persisted_counter.increment(20)
p persisted_counter.value
22
=> 22

# Decrement works much the same
persisted_counter.decrement
persisted_counter.value
=> 21

persisted_counter.decrement(6)
persisted_counter.value
=> 15

# Incrementing by anything other than integer will throw an ArgumentError
persisted_counter.increment "nonsense"
ArgumentError: Counters can only be incremented or decremented by integers.

That's about it. PN Counters in Riak are distributed, so each node will receive the proper increment/decrement operation. Enjoy using them.

Instrumentation

The Riak client has built-in event hooks for all major over-the-wire operations including:

  • riak.list_buckets
  • riak.list_keys
  • riak.set_bucket_props
  • riak.get_bucket_props
  • riak.clear_bucket_props
  • riak.get_index
  • riak.store_object
  • riak.get_object
  • riak.reload_object
  • riak.delete_object
  • riak.map_reduce
  • riak.ping

Events are propogated using ActiveSupport::Notifications, provided by the Instrumentable gem.

Enabling

Instrumentation is opt-in. If instrumentable is not available, instrumentation will not be available. To turn on instrumentation, simply require the instrumentable gem in your app's Gemfile:

gem 'instrumentable', '~> 1.1.0'

Then, to subscribe to events:

ActiveSupport::Notifications.subscribe(/^riak\.*/) do |name, start, finish, id, payload|
  name    # => String, name of the event (such as 'riak.get_object' from above)
  start   # => Time, when the instrumented block started execution
  finish  # => Time, when the instrumented block ended execution
  id      # => String, unique ID for this notification
  payload # => Hash, the payload
end

The payload for each event contains the following keys:

  • :client_id: The client_id of the Riak client
  • :_method_args: The array of method arguments for the instrumented method. For instance, for riak.get_object, this value would resemble [<Riak::Bucket ...>, 'key', {}]

Running Specs

We aim to have a comprehensive and fast set of tests, implemented using a modern, well-supported version of RSpec. These tests include both unit specs for individual classes, and integration specs that ensure the client works properly with an actual Riak instance.

The Riak Ruby Vagrant virtual machine's Riak configuration is normally used to test this client in development. Once it's up and running, configure the Ruby test_client.yml on the host machine to connect to pb_port: 17017 and test away.

Configuring the Riak node the tests connect to is done via the spec/support/test_client.yml file, which is loaded into a Ruby hash with symbolized keys, and passed to Riak::Client.new.

# test_client.yml
pb_port: 10017
# UNCOMMENT AUTHENTICATION SECTION WHEN RIAK HAS SECURITY ENABLED
# authentication:
#   user: user
#   password: password
#   ca_file: spec/support/certs/ca.crt

Spec dependencies

Specs depend on the following Riak configurations:

  • The LevelDB backend is necessary for testing secondary indexes.
  • allow_mult is required for many features: conflict resolution, and legacy counters among them.
  • Riak Search 2.0 ("Yokozuna") must be configured for testing full-text search.

The following bucket types are used during testing:

riak-admin bucket-type create counters '{"props":{"datatype":"counter", "allow_mult":true}}'
riak-admin bucket-type create other_counters '{"props":{"datatype":"counter", "allow_mult":true}}'
riak-admin bucket-type create maps '{"props":{"datatype":"map", "allow_mult":true}}'
riak-admin bucket-type create sets '{"props":{"datatype":"set", "allow_mult":true}}'
riak-admin bucket-type create yokozuna '{"props":{}}'

riak-admin bucket-type activate other_counters
riak-admin bucket-type activate counters
riak-admin bucket-type activate maps
riak-admin bucket-type activate sets
riak-admin bucket-type activate yokozuna

Client tests run both with and without security enabled, as we have to test several positive and negative paths. The tests broadly depend on these users and roles:

riak-admin security add-user user password=password
riak-admin security add-user certuser

riak-admin security add-source user 0.0.0.0/0 password
riak-admin security add-source certuser 0.0.0.0/0 certificate

riak-admin security grant riak_kv.get,riak_kv.put,riak_kv.delete,\
riak_kv.index,riak_kv.list_keys,riak_kv.list_buckets,\
riak_core.get_bucket,riak_core.set_bucket,\
riak_core.get_bucket_type,riak_core.set_bucket_type,\
search.admin,search.query,riak_kv.mapreduce on any to user

How to Contribute

  • Fork the project on Github. If you have already forked, use git pull --rebase to reapply your changes on top of the mainline. Example:

    $ git checkout master
    $ git pull --rebase basho master
  • Copy spec/support/test_server.yml.example to spec/support/test_server.yml and change that file according to your local installation of riak.

  • Create a topic branch. If you've already created a topic branch, rebase it on top of changes from the mainline "master" branch. Examples:

    • New branch:

      ``` bash
      $ git checkout -b topic
      ```
      
    • Existing branch:

      ``` bash
      $ git rebase master
      ```
      
  • 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.

  • Make your feature addition or bug fix. Make your specs and stories pass (green).

  • Run the suite using multiruby or rvm to ensure cross-version compatibility.

  • 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.

  • Commit, do not mess with Rakefile. If related to an existing issue in the tracker, include "Closes #X" in the commit message (where X is the issue number).

  • Send a pull request to the Basho repository.

License & Copyright

Copyright ©2010-2016 Sean Cribbs and Basho Technologies, Inc.

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

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

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

Auxillary Licenses

The included photo (spec/fixtures/cat.jpg) is Copyright ©2009 Sean Cribbs, and is licensed under the Creative Commons Attribution Non-Commercial 3.0 license. "Creative Commons"

gzip encoding support is copied from the ActiveSupport project.

Contributors

Thank you to all of our contributors!

riak-ruby-client's People

Stargazers

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

Watchers

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

riak-ruby-client's Issues

Use multi_json 1.1.0 for compatibility with Rails 3.2

Running into compatibility issues with Rails 3.2:

Bundler could not find compatible versions for gem "multi_json":
  In Gemfile:
    curator (>= 0) ruby depends on
      multi_json (~> 1.0.0) ruby

    uglifier (>= 1.0.3) ruby depends on
      multi_json (1.1.0)

Links to unsaved objects produce malformed link headers

"/buckets/#{escape(bucket)}" + (key.blank? ? "" : "/keys/#{escape(key)}")

If the linked object hasn't been saved, and therefore has a nil @key, riak-client tries to create a link of the form

"/buckets/", riaktag=type

Which Riak then rejects as malformed.

Shouldn't riak-client throw an exception if @key is blank?

Test server in master does not work on 0.14

Moved from https://github.com/seancribbs/ripple/issues/253 by @myronmarston:

I've got an app on riak 0.14 that I'm getting ready to upgrade to 1.0. The app is currently on 3595941 from the end of July, so I tried to upgrade ripple to see if it would work on 0.14.

I haven't tried it in production yet (so it may work there) but I rely on our test suite pretty heavily and I can't get the test server to work on 0.14. The test server would boot and immediately crash. Here's the log output, if that's useful. The test server backend looks like it declares that it's only compatible with riak 1.0 (or does API_VERSION refer to something else?).

I tried to hack riak-client so that it used the old test server backend and the old app.config and vm.args files that were being generated by the commit above. That fixed the crashing issue but then the test process would get stuck. I did some debugging and it appears to get stuck when attaching to the riak console. The console appeared to be waiting for input and the test process wasn't giving it any, so it just sat there.

Any ideas for how to get this to work?

Copied Comments

@myronmarston on November 29:

I decided to try upgrading riak and ripple at the same time, and ran in to problems there, too. I upgraded riak on my MBP using homebrew. Now when I start the test server, it crashes, but with a different error. I gisted the logs.

So for now I'm totally stuck on the upgrade since I can't get the ripple test server to work with either 0.14 or 1.0 :(.

@myronmarston on November 29:

With @seancribbs help we figured out that it was due to my use of localhost rather than 127.0.0.1 as the host. We should document and/or fix this.

FilterChain with and operator causes timeout

I have found that if you use the and operator in a filter chain Riak::Client will time out. I was using a filter chain similar to the example in the FilterBuilder docs http://seancribbs.com/ripple/Riak/MapReduce/FilterBuilder.html#

Here is the code:

require 'riak'
client = Riak::Client.new
mr = Riak::MapReduce.new(client)
mr.filter('bucket') do
  string_to_int
  AND do
    seq { greater_than 50 }
    seq { less_than    100 }
  end
end
mr.map("Riak.mapValuesJson", :keep => true)
mr.run

Will cause a timeout error:

Riak::MapReduceError: {"error":"timeout"}
from /Users/joseph/.rvm/gems/ruby-1.9.3-p125/gems/riak-client-1.0.4/lib/riak/map_reduce.rb:220:in `rescue in run'
from /Users/joseph/.rvm/gems/ruby-1.9.3-p125/gems/riak-client-1.0.4/lib/riak/map_reduce.rb:216:in `run'
from (irb):387
from /Users/joseph/.rvm/gems/ruby-1.9.3-p125/gems/railties-3.2.5/lib/rails/commands/console.rb:47:in `start'
from /Users/joseph/.rvm/gems/ruby-1.9.3-p125/gems/railties-3.2.5/lib/rails/commands/console.rb:8:in `start'
from /Users/joseph/.rvm/gems/ruby-1.9.3-p125/gems/railties-3.2.5/lib/rails/commands.rb:41:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'

Here is the MapReduce object output before calling run

#<Riak::MapReduce:0x007fcb9c14fea0 @query=[#<Riak::MapReduce::Phase:0x007fcb9d198870 @type=:map, @language="javascript", @function="Riak.mapValues", @keep=true, @arg=nil>], @inputs={:bucket=>"bucket", :key_filters=>[[:string_to_int], [:and, [[[:greater_than, 50]], [[:less_than, 100]]]]]}, @client=#<Riak::Client [<#Node 127.0.0.1:8098:8087>]>> 

However the following will not time out:

require 'riak'
client = Riak::Client.new
mr = Riak::MapReduce.new(client)
mr.filter('bucket') do
  string_to_int
  greater_than 50
end
mr.filter('bucket') do
  string_to_int
  less_than    100
end
mr.map("Riak.mapValuesJson", :keep => true)
mr.run

And the equivalent statement in Node.js works:

var riak = require('riak-js').getClient()
riak.add({bucket: 'bucket',
  key_filters:
  [["string_to_int"],
    ["and", [["greater_than", 50]],
    [["less_than", 100]]]]}).
  map('Riak.mapValuesJson').run();

PB client unnecessarily fetched object when storing object using prevent_stale_writes

Moved from https://github.com/seancribbs/ripple/issues/233 by @eliaslevy:

If you set prevent_stale_writes the PB client first fetches the key to compare the vector clocks itself. See beefcake_protobuffs_backend.rb: store_object.

But this is unnecessary and adds an additional round trip of latency to the request. Riak supports the if_not_modified options in the PB interface, which will let Riak perform the check itself.

See http://wiki.basho.com/PBC-Store-Object.html

Copied Comments

@seancribbs on October 29, 2011

I wanted to avoid it, but we'll need to make a get_server_info request to determine whether this option is even available. Unfortunately, if you send unknown fields in a PB message, the Erlang side will crash (known issue, not being fixed on 0.14.x).

TestServer w/ ets memory backend (2i, $keys, bin index, range search)

I found a case that uses $keys with 2i that fails, and it is likely that $bucket may also fail - using the Riak::TestServer. The search is fairly narrow and hard to catch - took me several hours to isolate.

I've built a reproducable test case and packaged it into a separate gem / tarball to make it easier to demonstrate. (emailing this to you).

unpack it; & bundle install

Current State:
I am using Riak::TestServer, using the leveldb backend. Running the following; a 2i search using $keys will pass.
ruby_opts=leveldb bundle exec rake

Desired State:
cleaning up the hacks; and using the riak_test_backend (which uses ets), the same test will fail.
ruby_opts=ets bundle exec rake

as far as tests in the riak-ruby-client; i noticed that:

https://github.com/basho/riak-ruby-client/blob/master/spec/support/unified_backend_examples.rb#L260

doesn't have any tests that cover $keys using a bin index; so I suspect (although I'm not great at erlang yet) that the relevant code that may have an issue is somewhere near here.

https://github.com/basho/riak-ruby-client/blob/master/erl_src/riak_kv_test_backend.erl#L486

Implicitly changing mapreduce result shape

I have run into some troubles because Riak::Client::BeefcakeProtobuffsBackend#mapred method changes result shape
based on the result itself.

The problem in this line:

block_given? || results.compact.size == 1 ? results.last : results

In my case I have mapred with 5 phases with two marked to be kept. In normal conditions runing this I have array of 5 elements.
But when last phase returns empty result, run will return just first phase data without wrapping array.

I understand that this is very convinient in most cases when we have only one last phase kept, but it could became a source of errors if we keep multiple phases and some of them can return empty results.

I think it would be better to make such decision based on mapred config, not result data. So if we have multiple phases kept
we always return wrapping array, and if we keep only one phase (as it is in most cases) we return it result without wrapping array.

I can make pull request if this sound reasonable.

Unexpected EOF on PBC socket when MR on a key that doesn't exist

I'm not sure what reasonable expected behavior is, but if the MR needs to return an error then it should do something nicer than "Unexpected EOF on PBC socket"

To reproduce:

mr = Riak::MapReduce.new(my_riak_client)
mr.add("existing_bucket", "non_existing_key").add("existing_bucket","existing_key").map("function(v){ return [];}", :keep => true).run

SocketError: Unexpected EOF on PBC socket
from /Users/tim/.rvm/gems/ree-1.8.7-2012.02/gems/riak-client-1.0.3/lib/riak/client/beefcake_protobuffs_backend.rb:129:in decode_response' from /Users/tim/.rvm/gems/ree-1.8.7-2012.02/gems/riak-client-1.0.3/lib/riak/client/beefcake_protobuffs_backend.rb:108:inmapred'
from /Users/tim/.rvm/gems/ree-1.8.7-2012.02/gems/riak-client-1.0.3/lib/riak/client.rb:323:in mapred' from /Users/tim/.rvm/gems/ree-1.8.7-2012.02/gems/riak-client-1.0.3/lib/riak/client.rb:433:inrecover_from'
from /Users/tim/.rvm/gems/ree-1.8.7-2012.02/gems/riak-client-1.0.3/lib/riak/client/pool.rb:126:in take' from /Users/tim/.rvm/gems/ree-1.8.7-2012.02/gems/riak-client-1.0.3/lib/riak/client.rb:431:inrecover_from'
from /Users/tim/.rvm/gems/ree-1.8.7-2012.02/gems/riak-client-1.0.3/lib/riak/client.rb:377:in protobuffs' from /Users/tim/.rvm/gems/ree-1.8.7-2012.02/gems/riak-client-1.0.3/lib/riak/client.rb:131:inbackend'
from /Users/tim/.rvm/gems/ree-1.8.7-2012.02/gems/riak-client-1.0.3/lib/riak/client.rb:322:in mapred' from /Users/tim/.rvm/gems/ree-1.8.7-2012.02/gems/riak-client-1.0.3/lib/riak/map_reduce.rb:217:inrun'
from (irb):65

Having the map return [v] also has problems.

Invalid store path when using a custom raw_name

It seems that the implementation of the http api is not correct. If you specify a custom raw_name in the riak database (for example "images"), the link header in a GET request on / will change from:

; rel="riak_kv_wm_buckets",
; rel="riak_kv_wm_buckets",
; rel="riak_kv_wm_index",
; rel="riak_kv_wm_keylist",
; rel="riak_kv_wm_link_walker",
; rel="riak_kv_wm_link_walker",
; rel="riak_kv_wm_mapred",
; rel="riak_kv_wm_object",
; rel="riak_kv_wm_object",
; rel="riak_kv_wm_ping",
; rel="riak_kv_wm_props",
; rel="riak_kv_wm_stats"

To:
; rel="riak_kv_wm_buckets",
; rel="riak_kv_wm_buckets",
; rel="riak_kv_wm_buckets",
; rel="riak_kv_wm_index",
; rel="riak_kv_wm_keylist",
; rel="riak_kv_wm_link_walker",
; rel="riak_kv_wm_link_walker",
; rel="riak_kv_wm_link_walker",
; rel="riak_kv_wm_mapred",
; rel="riak_kv_wm_object",
; rel="riak_kv_wm_object",
; rel="riak_kv_wm_object",
; rel="riak_kv_wm_ping",
; rel="riak_kv_wm_props",
; rel="riak_kv_wm_stats"

The http configuration (lib/riak/client/http_backend/configuration.rb) will pick the header link with the relationship "riak_kv_wm_buckets" and use it as a part of the url path.

Using the new /image path for storing object will then fail with an 405 error from the database. As mentioned at http://docs.basho.com/riak/1.2.1/references/apis/http/HTTP-Store-Object/ only "/riak" and "/buckets" are allowed right?

So my suggestion would be always to use /buckets as the root path for storing objects int the database.

Im using Riak Ruby Client version 1.1.1 with riak version 1.2.1

pbc client doesn't work with indexes

Whenever I try to assign an index to a RObject, it fails on store if I specify an index when the protocol is 'pbc'

        it "pbc client should work" do
            #the test works with :protocol => 'http'
            riak_client = Riak::Client.new(:protocol => 'pbc', :host => '127.0.0.1', :pb_port => 8081, :http_port => 8091)
            testval = SecureRandom.hex(64)
            riak_bucket = riak_client.bucket("test")
            riak_object = riak_bucket.get_or_new("test_key")
            puts riak_object.inspect
            riak_object.data = {:value => testval}

            #the test works without this line:
            riak_object.indexes = {"test_bin" => ["myindex"]}

            riak_object.store
            riak_object.reload
            puts riak_object.data
            newval = riak_object.data["value"]
            newval.should == testval
        end

error is

Fast Debugger (ruby-debug-ide 0.4.17.beta8, ruby-debug-base 0.11.30.pre10) listens on 127.0.0.1:58246
#<Riak::RObject {test,test_key} [application/json]:{"value"=>"ffe90958dcd76989bbdc86384db3ce717d720f2f3c1cad737f925b37be4ff1a362f16c85e520801df5aea162d808f2bee627bde1756c107222f794f4db20e581"}>

NoMethodError: undefinedod `<<' for nil:NilClass
.rvm/gems/ruby-1.9.3-p0@boat/gems/riak-client-1.0.0/lib/riak/client/beefcake/object_methods.rb:93:in `decode_index'
.rvm/gems/ruby-1.9.3-p0@boat/gems/riak-client-1.0.0/lib/riak/client/beefcake/object_methods.rb:61:in `block in load_content'
.rvm/gems/ruby-1.9.3-p0@boat/gems/riak-client-1.0.0/lib/riak/client/beefcake/object_methods.rb:61:in `each'
.rvm/gems/ruby-1.9.3-p0@boat/gems/riak-client-1.0.0/lib/riak/client/beefcake/object_methods.rb:61:in `load_content'
.rvm/gems/ruby-1.9.3-p0@boat/gems/riak-client-1.0.0/lib/riak/client/beefcake/object_methods.rb:44:in `load_object'
.rvm/gems/ruby-1.9.3-p0@boat/gems/riak-client-1.0.0/lib/riak/client/beefcake_protobuffs_backend.rb:159:in `decode_response'
.rvm/gems/ruby-1.9.3-p0@boat/gems/riak-client-1.0.0/lib/riak/client/beefcake_protobuffs_backend.rb:59:in `store_object'
.rvm/gems/ruby-1.9.3-p0@boat/gems/riak-client-1.0.0/lib/riak/client.rb:508:in `block in store_object'
.rvm/gems/ruby-1.9.3-p0@boat/gems/riak-client-1.0.0/lib/riak/client.rb:428:in `block in recover_from'
.rvm/gems/ruby-1.9.3-p0@boat/gems/riak-client-1.0.0/lib/riak/client/pool.rb:126:in `take'
.rvm/gems/ruby-1.9.3-p0@boat/gems/riak-client-1.0.0/lib/riak/client.rb:426:in `recover_from'
.rvm/gems/ruby-1.9.3-p0@boat/gems/riak-client-1.0.0/lib/riak/client.rb:374:in `protobuffs'
.rvm/gems/ruby-1.9.3-p0@boat/gems/riak-client-1.0.0/lib/riak/client.rb:128:in `backend'
.rvm/gems/ruby-1.9.3-p0@boat/gems/riak-client-1.0.0/lib/riak/client.rb:507:in `store_object'
.rvm/gems/ruby-1.9.3-p0@boat/gems/riak-client-1.0.0/lib/riak/robject.rb:179:in `store'
myproj/spec/models/riak_spec.rb:71:in `block (3 levels) in <top (required)>'
.rvm/gems/ruby-1.9.3-p0@boat/gems/rspec-core-2.8.0/lib/rspec/core/example.rb:80:in `instance_eval'
.rvm/gems/ruby-1.9.3-p0@boat/gems/rspec-core-2.8.0/lib/rspec/core/example.rb:80:in `block in run'
.rvm/gems/ruby-1.9.3-p0@boat/gems/rspec-core-2.8.0/lib/rspec/core/example.rb:173:in `with_around_hooks'
.rvm/gems/ruby-1.9.3-p0@boat/gems/rspec-core-2.8.0/lib/rspec/core/example.rb:77:in `run'
.rvm/gems/ruby-1.9.3-p0@boat/gems/rspec-core-2.8.0/lib/rspec/core/example_group.rb:355:in `block in run_examples'
.rvm/gems/ruby-1.9.3-p0@boat/gems/rspec-core-2.8.0/lib/rspec/core/example_group.rb:351:in `map'
.rvm/gems/ruby-1.9.3-p0@boat/gems/rspec-core-2.8.0/lib/rspec/core/example_group.rb:351:in `run_examples'
.rvm/gems/ruby-1.9.3-p0@boat/gems/rspec-core-2.8.0/lib/rspec/core/example_group.rb:337:in `run'
.rvm/gems/ruby-1.9.3-p0@boat/gems/rspec-core-2.8.0/lib/rspec/core/example_group.rb:338:in `block in run'
.rvm/gems/ruby-1.9.3-p0@boat/gems/rspec-core-2.8.0/lib/rspec/core/example_group.rb:338:in `map'
.rvm/gems/ruby-1.9.3-p0@boat/gems/rspec-core-2.8.0/lib/rspec/core/example_group.rb:338:in `run'
.rvm/gems/ruby-1.9.3-p0@boat/gems/rspec-core-2.8.0/lib/rspec/core/command_line.rb:28:in `block (2 levels) in run'
.rvm/gems/ruby-1.9.3-p0@boat/gems/rspec-core-2.8.0/lib/rspec/core/command_line.rb:28:in `map'
.rvm/gems/ruby-1.9.3-p0@boat/gems/rspec-core-2.8.0/lib/rspec/core/command_line.rb:28:in `block in run'
.rvm/gems/ruby-1.9.3-p0@boat/gems/rspec-core-2.8.0/lib/rspec/core/reporter.rb:34:in `report'
.rvm/gems/ruby-1.9.3-p0@boat/gems/rspec-core-2.8.0/lib/rspec/core/command_line.rb:25:in `run'
.rvm/gems/ruby-1.9.3-p0@boat/gems/rspec-core-2.8.0/lib/rspec/core/runner.rb:80:in `run_in_process'
.rvm/gems/ruby-1.9.3-p0@boat/gems/rspec-core-2.8.0/lib/rspec/core/runner.rb:69:in `run'
.rvm/gems/ruby-1.9.3-p0@boat/gems/rspec-core-2.8.0/lib/rspec/core/runner.rb:10:in `block in autorun'
.rvm/gems/ruby-1.9.3-p0@boat/gems/ruby-debug-ide-0.4.17.beta8/lib/ruby-debug-ide.rb:127:in `debug_load'
.rvm/gems/ruby-1.9.3-p0@boat/gems/ruby-debug-ide-0.4.17.beta8/lib/ruby-debug-ide.rb:127:in `debug_program'
.rvm/gems/ruby-1.9.3-p0@boat/gems/ruby-debug-ide-0.4.17.beta8/bin/rdebug-ide:95:in `<top (required)>'
-e:1:in `load'
-e:1:in `<main>'

My storage backend is eLevelDB and the HTTP client protocol works flawlessly.

TestServer: data/ssl_distribution.args_file

In the generated riak script, lines from vm.args are sed'd out into ssl_distribution.args_file in the test server's data directory (e.g. tmp/riak_test_server/data) - then we cd to the Riak install root (e.g. ~/riak-1.1.0/rel/riak/) and run nodetool, which expects "./data/ssl_distribution.args_file" - ergo, not the file that data was sed'd out into.

I build Riak from source, and don't have one of these files by default. Nor does the Mac homebrew install. This winds up being a speedbump for everyone running tests with riak.

Encoding::UndefinedConversionError: "\xE2" from ASCII-8BIT to UTF-8

Originally created in riak-ripple/ripple#128 (comment).

I am seeing this when using ':protocol => "pbc"':

Encoding::UndefinedConversionError: "\xE2" from ASCII-8BIT to UTF-8

Here's my setup:

$ ruby -v
jruby 1.7.2 (1.9.3p327) 2013-01-04 302c706 on OpenJDK 64-Bit Server VM 1.7.0_09-b30 [linux-amd64]

$ gem list

*** LOCAL GEMS ***

beefcake (0.3.7)
bouncy-castle-java (1.5.0146.1)
builder (3.1.4)
bundler (1.2.3)
celluloid (0.12.4)
excon (0.16.10)
facter (1.6.17)
i18n (0.6.1)
innertube (1.0.2)
jruby-launcher (1.0.15 java)
jruby-openssl (0.8.2)
json (1.7.6 java)
multi_json (1.5.0)
nokogiri (1.5.6 java)
rake (10.0.3)
redis (3.0.2)
rethinkdb (1.2.6.1)
riak-client (1.1.1)
ruby_protobuf (0.4.11)
rubygems-bundler (1.1.0)
rvm (1.11.3.6, 1.11.3.5)
spoon (0.0.1)
spoon_daemon (0.0.3)
timers (1.1.0)

Here's the stack trace:

Encoding::UndefinedConversionError: "\xE2" from ASCII-8BIT to UTF-8 -> 
org/jruby/RubyString.java:7563:in `encode', 
json/ext/GeneratorState.java:210:in `generate', 
/home/charl/.rvm/rubies/jruby-1.7.2/lib/ruby/1.9/json/common.rb:223:in `generate',
/home/charl/Projects/89n/analytics/thoth.git/lib/thoth/query.rb:282:in `get', 
/home/charl/Projects/89n/analytics/thoth.git/lib/thoth/query.rb:346:in `process', 
org/jruby/RubyKernel.java:1809:in `public_send', 
/home/charl/.rvm/gems/jruby-1.7.2@thoth/gems/celluloid-0.12.4/lib/celluloid/calls.rb:23:in `dispatch', 
/home/charl/.rvm/gems/jruby-1.7.2@thoth/gems/celluloid-0.12.4/lib/celluloid/actor.rb:327:in `handle_message', 
/home/charl/.rvm/gems/jruby-1.7.2@thoth/gems/celluloid-0.12.4/lib/celluloid/tasks/task_fiber.rb:24:in `initialize', 
/home/charl/.rvm/gems/jruby-1.7.2@thoth/gems/celluloid-0.12.4/lib/celluloid/tasks/task_fiber.rb:23:in `initialize'

HTTP (with or without Excon) works perfectly.

1.0.0.beta on rubygems doesn't work with 1.8

http://rubygems.org/gems/riak-client/versions/1.0.0.beta uses syntax that doesn't work in Ruby 1.8:

/Users/bkerley/.rbenv/versions/ree-1.8.7-2011.12/lib/ruby/gems/1.8/gems/riak-client-1.0.0.beta/lib/riak.rb:3:in `require': /Users/bkerley/.rbenv/versions/ree-1.8.7-2011.12/lib/ruby/gems/1.8/gems/riak-client-1.0.0.beta/lib/riak/client.rb:89: odd number list for Hash (SyntaxError)
        prefix: options[:prefix] || "/riak/",
               ^
/Users/bkerley/.rbenv/versions/ree-1.8.7-2011.12/lib/ruby/gems/1.8/gems/riak-client-1.0.0.beta/lib/riak/client.rb:89: syntax error, unexpected ':', expecting '}'
        prefix: options[:prefix] || "/riak/",
               ^
/Users/bkerley/.rbenv/versions/ree-1.8.7-2011.12/lib/ruby/gems/1.8/gems/riak-client-1.0.0.beta/lib/riak/client.rb:89: syntax error, unexpected ',', expecting kEND
/Users/bkerley/.rbenv/versions/ree-1.8.7-2011.12/lib/ruby/gems/1.8/gems/riak-client-1.0.0.beta/lib/riak/client.rb:90: syntax error, unexpected ',', expecting kEND
/Users/bkerley/.rbenv/versions/ree-1.8.7-2011.12/lib/ruby/gems/1.8/gems/riak-client-1.0.0.beta/lib/riak/client.rb:91: syntax error, unexpected ',', expecting kEND
/Users/bkerley/.rbenv/versions/ree-1.8.7-2011.12/lib/ruby/gems/1.8/gems/riak-client-1.0.0.beta/lib/riak/client.rb:93: syntax error, unexpected '}', expecting kEND
      }.merge(options[:http_paths] || {})
       ^

The gemspec for the gem should set a required ruby version:

  s.required_ruby_version = '~> 1.9.2'

Searching non-existing documents in indexes

Hello,

I'm gaining my first experience with Ripple and riak-ruby-client and get an error, I can't get rid of:

When I have a simple model like:

require 'ripple'
class Article
  include Ripple::Document
  include Exceptions
  self.bucket_name = 'articles'

  property :article_no,    String, :presence => true
  property :name,         String, :presence => true
  # property :unitCode,     String, :presence => true
  # property :EAN,          String, :presence => true
  # property :brand,        String, :presence => true

  before_save :check_article_no_uniquness
  after_save :add_to_index

  private

  def add_to_index
    Ripple.client.index 'article_numbers', {id: key, article_no: article_no}
  end

  def check_article_no_uniquness
    unless Article.find_by_article_no(article_no).nil?
      raise Exceptions::DuplicateArticleNoError      
    end
  end

  public

  def self.find_by_article_no(article_no)
    result = Ripple.client.search("article_numbers", "article_no:#{article_no}")
    key  = result['response']['docs'].first.try(:[], 'id')
    if key
      Rails.logger.info "key #{key} found."
      doc = Article.find(key)
    else
      doc = nil
    end  
  end
end

save one article (:article_no => 4711, :name => "foo")

And I try to search for a document in an index by

Ripple.client.search("article_numbers", "article_no:4712")

Or

Article.find_by_article_no("4712")

I get an error in

riak-client-1.0.4/lib/riak/client/net_http_backend.rb:58 .

Riak responds with 400.

The stored Document can be loaded without problems

Ripple.client.search("article_numbers", "article_no:4711")

Or

Article.find_by_article_no("4711")

but the non-existing document raises an exception. I'd expected nil as the result.

Is this intended that way ?

Thanks,
Andy

adding CRDT support

Hey @seancribbs ,
I cringe to ask, as I know this is not trivial, plus I know that you all at Basho are working on it! I wanted to throw a place holder up here in case others like myself have questions.

Is there a rough ETA on something like this? Even if it is vague like mid-level priority, q3 of this year, etc would be helpful. We want to move more towards riak but this is a blocker for us. While we might be able to figure something out on our own (perhaps..., or mostly until we hit an edge case that kills us), if this is somewhere close to being merged into riak-ruby-client, we'll hold off.

is pulling in something like meangirls an option?

thanks!!!

Limiting number of insertions

I'm reading a file with 2000+ lines where each line is a new record.

I save it on my SQLite and there is a callback, before_save that calls a function that saves the same values on Riak.

Every time it saves only 730 records on Riak and since I'm using transaction, all SQLite records are deleted.

If I remove the callback all the 2000+ records are saved on SQLite.

There is this message on Riak console, it shows after inserting 730 records:

17:31:11.716 [error] CRASH REPORT Process <0.157.0> with 0 neighbours crashed with reason: {error,accept_failed}
17:31:11.982 [error] {mochiweb_socket_server,310,{acceptor_error,{error,accept_failed}}}

Here is a example of what I'm trying to save:

281-085-675
281-085-676
281-085-677
281-085-678
281-085-679
281-085-680
281-085-681
281-085-542
281-085-543
281-085-544
281-085-545
281-085-546

SSL connections not functional

Sample config:

production:
protocol: https
verify_mode: "none"
http_port: 8000
http_backend: Excon
pb_port: 8087
nodes:
-
host: 10.183.38.200 # prod-riak-r01
-
host: 10.183.41.31 # prod-riak-r02
-
host: 10.183.41.32 # prod-riak-r03

The behavior:

  • I can see packets pass outbound from a client initiated connection, and they are encrypted
  • I can see those packets arrive at the riak node
  • beyond that, there is no communication between client and server, and any requests simply hang

Notes via IRC:

seancribbs: heffergm: so it seems there's a bug in the ssl_options stuff. I though adamhunter fixed it, but maybe we let a regression creep in. will you open an issue on GH? https://github.com/basho/riak-ruby-client/issues ?

Riak 1.4 Feature: Protocol Buffers Bucket Properties

Riak 1.4 adds all known, standard bucket properties to the Protocol
Buffers transport for setting and getting, and adds the "reset" or
"clear" feature as well.

  • Support all bucket properties over PB
  • Support "reset" or "clear" request/response over PB

Set Backend Prop on Bucket

I am configuring a multi backend storage for my 5 riak nodes.

I want to create several buckets with each one having a different backend. The documentation shows 2 methods of doing this, one through the Erlang console and the other using the HTTP REST API.

Is there a way to do this in the ruby client?

Thanks

Document best ways to configure and handle error situations.

From reading the docs and the wiki, it's not entirely clear to me what will happen when things are not running smoothly on a cluster, and what I should do to rectify it. The questions that immediately jump to mind are:

  1. If I specify 6 nodes in my client, and a connection is refused during a get (say, because a box is down), will riak-client retry that request against a different node if my r is less than my n? If not, is there anything I can do from the client side to retry that get and ensure it hits a different node the second time?
  2. How can I configure timeouts for basic operations like robject#store? Sometimes a write will fail if the box it is writing to goes down - but it won't fail quickly, it will 503 Timeout, which is usually longer than our gateway timeout, so the user just gets an haproxy error page. I'd like our engineering team to be able to be smart about setting different timeouts in different places based on the operation at hand, but I'm not sure what the right way is to do this.

Unexpected EOF on PBC Socket

Moved from https://github.com/seancribbs/ripple/issues/152 by @myronmarston:

I get this error when I try to use the pbc protocol:

Expected success from Riak but received server_error. Unexpected EOF on PBC socket
/Users/myron/.rvm/gems/ruby-1.9.2-p180@silo/bundler/gems/ripple-bd9c61dcfda3/riak-client/lib/riak/client/beefcake_protobuffs_backend.rb:172:in `rescue in decode_response'
/Users/myron/.rvm/gems/ruby-1.9.2-p180@silo/bundler/gems/ripple-bd9c61dcfda3/riak-client/lib/riak/client/beefcake_protobuffs_backend.rb:129:in `decode_response'
/Users/myron/.rvm/gems/ruby-1.9.2-p180@silo/bundler/gems/ripple-bd9c61dcfda3/riak-client/lib/riak/client/beefcake_protobuffs_backend.rb:59:in `store_object'
/Users/myron/.rvm/gems/ruby-1.9.2-p180@silo/bundler/gems/ripple-bd9c61dcfda3/riak-client/lib/riak/robject.rb:133:in `store'
/Users/myron/.rvm/gems/ruby-1.9.2-p180@silo/bundler/gems/ripple-bd9c61dcfda3/ripple/lib/ripple/document/persistence.rb:65:in `really_save'
/Users/myron/.rvm/gems/ruby-1.9.2-p180@silo/bundler/gems/ripple-bd9c61dcfda3/ripple/lib/ripple/callbacks.rb:43:in `block in really_save'
/Users/myron/.rvm/gems/ruby-1.9.2-p180@silo/bundler/gems/ripple-bd9c61dcfda3/ripple/lib/ripple/callbacks.rb:51:in `block (2 levels) in run_save_callbacks'
/Users/myron/.rvm/gems/ruby-1.9.2-p180@silo/gems/activesupport-3.0.6/lib/active_support/callbacks.rb:419:in `_run_create_callbacks'
/Users/myron/.rvm/gems/ruby-1.9.2-p180@silo/gems/activesupport-3.0.6/lib/active_support/callbacks.rb:94:in `run_callbacks'
/Users/myron/.rvm/gems/ruby-1.9.2-p180@silo/bundler/gems/ripple-bd9c61dcfda3/ripple/lib/ripple/callbacks.rb:50:in `block in run_save_callbacks'
/Users/myron/.rvm/gems/ruby-1.9.2-p180@silo/gems/activesupport-3.0.6/lib/active_support/callbacks.rb:439:in `_run_save_callbacks'
/Users/myron/.rvm/gems/ruby-1.9.2-p180@silo/gems/activesupport-3.0.6/lib/active_support/callbacks.rb:94:in `run_callbacks'
/Users/myron/.rvm/gems/ruby-1.9.2-p180@silo/bundler/gems/ripple-bd9c61dcfda3/ripple/lib/ripple/callbacks.rb:49:in `run_save_callbacks'
/Users/myron/.rvm/gems/ruby-1.9.2-p180@silo/bundler/gems/ripple-bd9c61dcfda3/ripple/lib/ripple/callbacks.rb:42:in `really_save'
/Users/myron/.rvm/gems/ruby-1.9.2-p180@silo/bundler/gems/ripple-bd9c61dcfda3/ripple/lib/ripple/document/persistence.rb:59:in `save'
/Users/myron/.rvm/gems/ruby-1.9.2-p180@silo/bundler/gems/ripple-bd9c61dcfda3/ripple/lib/ripple/attribute_methods/dirty.rb:12:in `save'
/Users/myron/.rvm/gems/ruby-1.9.2-p180@silo/bundler/gems/ripple-bd9c61dcfda3/ripple/lib/ripple/validations.rb:49:in `save'
/Users/myron/.rvm/gems/ruby-1.9.2-p180@silo/bundler/gems/ripple-bd9c61dcfda3/ripple/lib/ripple/validations.rb:55:in `save!'

Copied Comments

@seancribbs on October 12, 2011:

@myronmarston Will you try this out again and verify that you do/do not receive this error?

@newtonapple on October 20, 2011:

I'm getting a similar error for pbc when I use the Riak::Client.

b = Riak::Client.new(:protocol => 'pbc', :pb_port=> 8093).bucket('test')
b.get('foo') # nil
b.get('foo') 

Riak::ProtobuffsFailedRequest: Expected success from Riak but received server_error. Unexpected EOF on PBC socket
    from /Users/ddai/.rvm/gems/ruby-1.9.2-p290/gems/riak-client-0.9.8/lib/riak/client/beefcake_protobuffs_backend.rb:185:in `rescue in decode_response'
    from /Users/ddai/.rvm/gems/ruby-1.9.2-p290/gems/riak-client-0.9.8/lib/riak/client/beefcake_protobuffs_backend.rb:142:in `decode_response'
    from /Users/ddai/.rvm/gems/ruby-1.9.2-p290/gems/riak-client-0.9.8/lib/riak/client/beefcake_protobuffs_backend.rb:52:in `fetch_object'
    from /Users/ddai/.rvm/gems/ruby-1.9.2-p290/gems/riak-client-0.9.8/lib/riak/bucket.rb:102:in `get'
    from (irb):9
    from /Users/ddai/.rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `<main>'

b.get('foo') # nil
b.get('foo') # same error again (and cycles between nil and error)

Note that I've previously set 'foo' using the HTTP client. HTTP client seems to be fine.

@seancribbs on October 20, 2011:

Thanks @newtonapple. "Unexpected EOF" generally means that the server disconnected, I'll look into it.

@marcheiligers on December 15, 2011

We're also seeing this error. We found that the first request after a short break always fails with this error. Subsequent requests then work if they are soon enough after that first request. As a horrific workaround we added this to beefcake_protobuffs_backend (riak-client 1.0.0.beta, we've vendored this and Risky in our app):

      MAX_RETRIES = 3
      def with_retries
        retries = 0
        result = nil

        begin
          result = yield
        rescue Riak::ProtobuffsFailedRequest => e
          raise if e.not_found?
          retries += 1
          if retries < MAX_RETRIES
            retry
          else
            raise
          end
        end

        result
      end

And then surrounded the various calls with:

        with_retries do
          write_protobuff(:PutReq, req)
          decode_response(robject)
        end

This works and I've not seen more than one retry and again only on the first request after a short time. My guess is that the reset_socket actually resolves the issue.

Also, we're using haproxy and found this: http://lists.basho.com/pipermail/riak-users_lists.basho.com/2011-May/004389.html

We've not had a chance to try this yet, but the problem may not be with beefcake, the protobuffs implementation or even riak-client, but just an haproxy configuration thing.

@seancribbs on December 15, 2011

@marcheiligers That's much more helpful. My theory then is better confirmed. We should likely check the state of the socket before writing, to be sure that the server has not disconnected.

Also note that with the master branch this is handled transparently by trying another connection in the pool.

Riak::TestServer config requirements do not make sense?

hello,

It seems that setting up a Riak::TestServer is not possible out of the box in riak-client-1.1.1?

If I use something similar to ripple (note, I'm using riak 1.3.0-rc4 and use ps to find the one I'm running; I have riak installed via homebrew but want to use a version of riak in a non-standard location; though using the homebrew version doesn't seem to work either):

`ps aux | grep riak | grep -v grep | awk '{print $11}'`.split.first =~ /^(\/.+\/riak.+?)\//
riak_loc = $1
riak_test_server = Riak::TestServer.new :root => "#{Rails.root}/tmp/riak_test_server", :source => "#{riak_loc}/bin", :min_port => 15000

and all sorts of failures start:

  • if I run it as so, lib/riak/version.rb#configure_base_dir returns nil. There seems to be an odd dependency on :source being set to /^RUNNER_BASE_DIR=(.*)/, which seems odd.
  • if I set `:source => "RUNNER_BASE_DIR=#{riak_loc}/bin" (which seems wrong, but to play along...") but fails anyway because in lib/riak/configuration.rb#configure_paths, it is setup as a Pathname, which causes it to add pwd to the path name since it isn't a valid unix path...so scratching that.... (and with #configure_base_dir still returning nil
  • and then I notice lib/riak/version.rb is using source, but then expects to be able to point to releases/start_erl.data, and I have no idea how it is to get there because if I do not append the riak loc with bin/, it fails elsewhere as it cannot find the riak binary.

I found very very little in the way of documentation so I'm not totally sure this is a code issue, a documentation issue, or a me issue ;). honestly, I can't quite tell...

to me, something like this would make more sense:
riak_test_server = Riak::TestServer.new :binary_folder => "#{riak_loc}/bin" #use /tmp or whatever Ruby TempFile's Dir.tmpdir sets up

anyway, let me know if you would like me to dive in and provide a patch, if this indeed is a code issue

ProtobuffsFailedRequest takes a code but its not available from the object [JIRA: CLIENTS-888]

Moved from https://github.com/seancribbs/ripple/issues/234 by @eliaslevy:

ProtobuffsFailedRequest takes an error code as an argument, but the code is not available from the object, as its not used by the FailedRequest base class.

Similarly the message is passed as the :body argument to the parent, but the parent those not use it or store it.

Presumably the code for managing the error code and original message where going to be moved from the HTTPFailedRequest class to its parent the FailedRequest class, so that these would be available to both HTTPFailedRequest and ProtobuffsFailedRequest.

Copied Comments

@seancribbs on October 30, 2011

The code is not available because there are only two options, :not_found and :server_error, which are both exposed as "query" methods (and are semantically more useful in my opinion). The message becomes part of the exception's message (accessible as defined on the Exception class), via super.

The two Riak interfaces are not 100% feature-equivalent, and therefore they are not treated equally.

@seancribbs closed the issue on October 30, 2011

@eliaslevy on October 30, 2011

There is at least one more possible code: :stale_object.
From beefcake_protobuffs_backend.rb:store_object,

raise Riak::ProtobuffsFailedRequest(:stale_object, t(
"stale_write_prevented")) unless other.vclock == robject.vclock

You may want to add stale_object as another query method. As it is, at the
moment I have to dig into the exception instance variable to get the
original message to compare it to "stale_write_prevented", and that may not
work if the string is replaced by something else by the
internationalization code.

@eliaslevy on October 30, 2011

Sean, given my comment above, do you consider this issue closed?

@seancribbs reopened the issue on October 30, 2011

@seancribbs on October 30, 2011

Reopened. Next time you should just say what you're trying to accomplish, it'll help me better understand what the problem is.

Bloated gem: 17MiB instead of 80-250KiB.

The gem for riak-ruby-client should be anywhere from 80-250KiB depending on whether one believes in including test files.

This is due to the severely broken attempt at parsing and applying (a subset of) .gitignore semantics.

.gitignore is a VERY tricky mechanism to apply properly. To demonstrate, here's some sample code that almost emulates "git ls-files -- ." from project root, with several major caveats (see the gist for details):

https://gist.github.com/3868993

This is code that APPROACHES correct behavior here, but is by no means complete and may be buggy. I have a fairly extensive set of test cases for the features I implement, but I probably have missed a few.

Simulate multi-get via parallel gets

It was decided in the Clients mumble that we would support multi-get operations via parallelizing regular gets in the client libraries. At a later date, we can decide whether this operation will be more efficient to do on the server-side.

Bucket.is_indexed? results in: NoMethodError: undefined method `include?' for nil:NilClass

Here's an irb session illustrating the issue:

require "riak"; client = Riak::Client.new(:protocol => "pbc"); bucket_name = "tweets"; bucket = client.bucket bucket_name
=> #<Riak::Bucket {tweets}>
irb(main):003:0> 
irb(main):004:0* 
irb(main):005:0* bucket.props
=> {"n_val"=>3, "allow_mult"=>false}
irb(main):006:0> bucket.is_indexed?
NoMethodError: undefined method `include?' for nil:NilClass
    from /home/charl/.rvm/gems/jruby-1.7.2@thoth/gems/riak-client-1.1.1/lib/riak/bucket.rb:208:in `is_indexed?'
    from (irb):6:in `evaluate'
    from org/jruby/RubyKernel.java:1066:in `eval'
    from org/jruby/RubyKernel.java:1392:in `loop'
    from org/jruby/RubyKernel.java:1174:in `catch'
    from org/jruby/RubyKernel.java:1174:in `catch'
    from /home/charl/.rvm/rubies/jruby-1.7.2/bin/irb:13:in `(root)'

I've submitted #88.

Resolve symlinks for the riak script

When using homebrew:

cat: /usr/local/releases/start_erl.data: No such file or directory
db/test/1/bin/riak: line 84: /usr/local/erts-/bin/escript: No such file or directory
db/test/1/bin/riak: line 219: /usr/local/erts-/bin/escript: No such file or directory
Error reading /workspace/db/test/1/etc/app.config

with riak 1.0.3

Javascript syntax error in map() does not raise an error

The following map does not present an error as expected and is misleading in that it reports an empty data set as the result:

irb(main):432:0* entries = Riak::MapReduce.new(client).search("tweets", (["id_str"] * ids.length).zip(ids).map {|t| t.join ":"}.join(" OR ")).map("function(v){ t = JSON.parse(v.values[0].data); if (t.entities.hashtags) { return [t]; } else { return [];  }", :keep => true).run
=> []

Adding the missing '}' gets the query to run successfully.

I see the following errors in the riak logs:

==> /var/log/riak/console.log <==
2013-02-06 22:07:26.747 [error] <0.435.0>@riak_pipe_vnode:new_worker:768 Pipe worker startup failed:fitting was gone before startup

==> /var/log/riak/error.log <==
2013-02-06 22:07:26.747 [error] <0.435.0>@riak_pipe_vnode:new_worker:768 Pipe worker startup failed:fitting was gone before startup

Riak test server not working in v1.0.0 with Riak 1.1.0

I can't get the test server to work properly with the changes in #16 and Riak 1.1.0. I get the following exception when trying to start the test server:

#<Errno::ENOENT: No such file or directory - /usr/local/releases/start_erl.data>

I tried to track the error down and it seems like Riak::Node#version method tries to figure out the Erlang version (and failing), when it should really be trying to figure out the Riak version.

I am using Homebrew on OS X.

Fallback consistency parameters and command reissue

I think it would be useful to be able to specify fallback consistency parameters to CRUD commands, and the the client to reissue a command automatically if the cluster status can't meet the initial consistency parameters, while somehow indicating this has happened to the caller.

Often one is working with data that we'd prefer to access in the usual case using strong consistency, but that are willing accept eventual consistency if the cluster is split or degraded.

Having the client do this would make this quite DRY.

JSON serializer does not respect the max_nesting option on jruby.

I see this in a spec failure when running on jruby. I took a cursory look at java implementation of the json gem but nothing jumped out at me.

robby@local:~/workspace/personal/riak-ruby-client(master) rvm use jruby-1.6.7.2
Using /Users/robby/.rvm/gems/jruby-1.6.7.2

robby@local:~/workspace/personal/riak-ruby-client(master) bundle install
Using rake (0.9.2.2) 
Using beefcake (0.3.7) 
Using bouncy-castle-java (1.5.0146.1) 
Using builder (3.0.0) 
Using bundler (1.1.5) 
Using diff-lcs (1.1.3) 
Using excon (0.16.1) 
Using fakeweb (1.3.0) 
Using ffi (1.1.5) 
Using growl (1.0.3) 
Using rb-fchange (0.0.5) 
Using rb-fsevent (0.9.1) 
Using rb-inotify (0.8.8) 
Using listen (0.4.7) 
Using thor (0.16.0) 
Using guard (1.3.2) 
Using guard-rspec (1.2.1) 
Using i18n (0.6.0) 
Using innertube (1.0.2) 
Using jruby-openssl (0.7.7) 
Using json (1.7.4) 
Using multi_json (1.3.6) 
Using rack (1.4.1) 
Using riak-client (1.0.4) from source at . 
Using rspec-core (2.10.1) 
Using rspec-expectations (2.10.0) 
Using rspec-mocks (2.10.1) 
Using rspec (2.10.0) 
Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.

robby@local:~/workspace/personal/riak-ruby-client(master) rake spec
/Users/robby/.rvm/rubies/jruby-1.6.7.2/bin/jruby -S rspec ./spec/integration/riak/cluster_spec.rb ./spec/integration/riak/http_backends_spec.rb ./spec/integration/riak/node_spec.rb ./spec/integration/riak/protobuffs_backends_spec.rb ./spec/integration/riak/test_server_spec.rb ./spec/integration/riak/threading_spec.rb ./spec/riak/beefcake_protobuffs_backend_spec.rb ./spec/riak/bucket_spec.rb ./spec/riak/client_spec.rb ./spec/riak/escape_spec.rb ./spec/riak/excon_backend_spec.rb ./spec/riak/feature_detection_spec.rb ./spec/riak/headers_spec.rb ./spec/riak/http_backend_spec.rb ./spec/riak/link_spec.rb ./spec/riak/map_reduce_spec.rb ./spec/riak/multipart_spec.rb ./spec/riak/net_http_backend_spec.rb ./spec/riak/node_spec.rb ./spec/riak/robject_spec.rb ./spec/riak/search_spec.rb ./spec/riak/serializers_spec.rb ./spec/riak/stamp_spec.rb ./spec/riak/stream_parser_spec.rb ./spec/riak/walk_spec_spec.rb ./spec/riak/beefcake_protobuffs_backend/object_methods_spec.rb ./spec/riak/core_ext/to_param_spec.rb ./spec/riak/http_backend/configuration_spec.rb ./spec/riak/http_backend/object_methods_spec.rb ./spec/riak/http_backend/transport_methods_spec.rb ./spec/riak/map_reduce/filter_builder_spec.rb ./spec/riak/map_reduce/phase_spec.rb --profile --tag ~integration --tag ~slow
Run options:
  include {:focus=>true}
  exclude {:integration=>true, :slow=>true}

All examples were filtered out; ignoring {:focus=>true}
.......................................................................................................................................................................................................................F.........................................................................................................................................................................................................................................................................................................................................................................................................................................................................

Retried examples: 0


Failures:

  1) Riak::Serializers JSON serializer respects the max nesting option
     Failure/Error: expect {
       expected no Exception, got #<MultiJson::DecodeError: nesting of 20 is too deep>
     # ./spec/riak/serializers_spec.rb:62:in `(root)'

Finished in 3.55 seconds
673 examples, 1 failure

Failed examples:

rspec ./spec/riak/serializers_spec.rb:46 # Riak::Serializers JSON serializer respects the max nesting option

Randomized with seed 1345153568

rake aborted!
/Users/robby/.rvm/rubies/jruby-1.6.7.2/bin/jruby -S rspec ./spec/integration/riak/cluster_spec.rb ./spec/integration/riak/http_backends_spec.rb ./spec/integration/riak/node_spec.rb ./spec/integration/riak/protobuffs_backends_spec.rb ./spec/integration/riak/test_server_spec.rb ./spec/integration/riak/threading_spec.rb ./spec/riak/beefcake_protobuffs_backend_spec.rb ./spec/riak/bucket_spec.rb ./spec/riak/client_spec.rb ./spec/riak/escape_spec.rb ./spec/riak/excon_backend_spec.rb ./spec/riak/feature_detection_spec.rb ./spec/riak/headers_spec.rb ./spec/riak/http_backend_spec.rb ./spec/riak/link_spec.rb ./spec/riak/map_reduce_spec.rb ./spec/riak/multipart_spec.rb ./spec/riak/net_http_backend_spec.rb ./spec/riak/node_spec.rb ./spec/riak/robject_spec.rb ./spec/riak/search_spec.rb ./spec/riak/serializers_spec.rb ./spec/riak/stamp_spec.rb ./spec/riak/stream_parser_spec.rb ./spec/riak/walk_spec_spec.rb ./spec/riak/beefcake_protobuffs_backend/object_methods_spec.rb ./spec/riak/core_ext/to_param_spec.rb ./spec/riak/http_backend/configuration_spec.rb ./spec/riak/http_backend/object_methods_spec.rb ./spec/riak/http_backend/transport_methods_spec.rb ./spec/riak/map_reduce/filter_builder_spec.rb ./spec/riak/map_reduce/phase_spec.rb --profile --tag ~integration --tag ~slow failed

Tasks: TOP => spec
(See full trace by running task with --trace)

robby@local:~/workspace/personal/riak-ruby-client(master) 

Gem release includes .rbc (rubinius) files

% du -hs cache/riak-client-1.0.3.gem 
5.8M    cache/riak-client-1.0.3.gem
% du -hs gems/riak-client-1.0.3 
1.0M    gems/riak-client-1.0.3
% find gems/riak-client-1.0.3 -name '*.rbc'  | wc -l
58

Two odd notes.

  • The gem is 5.8mb, but installed only occupies 1mb installed.
  • The gem includes many rubinius artifact files (.rbc files)

Is this intentional? If so, feel free to close immediately :)

MapReduce URI-escapes buckets/keys

Moved from https://github.com/seancribbs/ripple/issues/235 by @outoftime:

I noticed that the MapReduce class calls escape all over the place when constructing itself, which is implemented as CGI#escape. However, the MapReduce phase executes by POSTing a JSON document to Riak, so I don't think it makes sense to URI-escape the values. After noticing this as a problem when constructing my own map/reduce with keys that contain URI-unsafe characters, I got it working easily enough by overriding MapReduce#escape to just return the value untouched.

I'm happy to submit a patch for this; just wanted to confirm that my instincts are correct first. Thanks!

Copied Comments

@seancribbs on November 10, 2011

If you are using Riak 1.0 do Riak.url_escaping = true and this will not occur.

Add a socket timeout option to handle lingering socket requests on transitioning or mixed state nodes [JIRA: CLIENTS-624]

There have been several recent problems with nodes in various states transition whether it be physical or service(Riak) being up where the riak-ruby-client will try and connect for upwards of 60s or more, until if fails and tries the next node in the node list. Some sites have been able to fix this by tweaking DNS. Suggested recommendation was to add an option to define a socket level timeout to provide more predictable behavior.

Search data structure is different from MapReduce one

When I use search() to grab a hierarchical JSON document I get the following data structure back that seems to have flattened the multi-level hash by key concatenation:

{"docs"=>[{"id"=>"300145260363931648", "created_at"=>"Sat Feb 09 07:33:01 +0000 2013", "entities_urls_display_url"=>"nakkaya.com/2009/09/28/fra\xE2\x80\xA6", "entities_urls_expanded_url"=>"http://nakkaya.com/2009/09/28/fractals-in-clojure-fractal-fern/", "entities_urls_indices"=>"36 56", "entities_urls_url"=>"http://t.co/u0k3iDBX", "favorited"=>"false", "id_str"=>"300145260363931648", "metadata_iso_language_code"=>"en", "metadata_result_type"=>"recent", "possibly_sensitive"=>"false", "retweet_count"=>"0", "retweeted"=>"false", "source"=>"web", "text"=>"Fractals in Clojure \xE2\x80\x94 Fractal Fern:\nhttp://t.co/u0k3iDBX", "truncated"=>"false", "user_contributors_enabled"=>"false", "user_created_at"=>"Thu Mar 31 18:34:08 +0000 2011", "user_default_profile"=>"true", "user_default_profile_image"=>"false", "user_description"=>"I'm an artificial intelligence programmer, meta-programmer, mathematician, proactive singularitarian/transhumanist/extropian, extreme skater, and guitar player.", "user_entities_url_urls_indices"=>"0 28", "user_entities_url_urls_url"=>"http://jckuri2005.comyr.com/", "user_favourites_count"=>"31", "user_followers_count"=>"107", "user_friends_count"=>"210", "user_geo_enabled"=>"false", "user_id"=>"275148936", "user_id_str"=>"275148936", "user_is_translator"=>"false", "user_lang"=>"en", "user_listed_count"=>"9", "user_location"=>"Guayaquil, Ecuador", "user_name"=>"Juan Carlos Kuri", "user_profile_background_color"=>"C0DEED", "user_profile_background_image_url"=>"http://a0.twimg.com/images/themes/theme1/bg.png", "user_profile_background_image_url_https"=>"https://si0.twimg.com/images/themes/theme1/bg.png", "user_profile_background_tile"=>"false", "user_profile_banner_url"=>"https://si0.twimg.com/profile_banners/275148936/1352479045", "user_profile_image_url"=>"http://a0.twimg.com/profile_images/3101684315/54553879e8ab2585eadf470d53f7dd4f_normal.png", "user_profile_image_url_https"=>"https://si0.twimg.com/profile_images/3101684315/54553879e8ab2585eadf470d53f7dd4f_normal.png", "user_profile_link_color"=>"0084B4", "user_profile_sidebar_border_color"=>"C0DEED", "user_profile_sidebar_fill_color"=>"DDEEF6", "user_profile_text_color"=>"333333", "user_profile_use_background_image"=>"true", "user_protected"=>"false", "user_screen_name"=>"jckuri", "user_statuses_count"=>"1929", "user_time_zone"=>"Central Time (US & Canada)", "user_url"=>"http://jckuri2005.comyr.com/", "user_utc_offset"=>"-21600", "user_verified"=>"false"}], "max_score"=>0.35355299711227417, "num_found"=>1}

Here's what the top level of keys look like for this search query:

irb(main):027:0* client.search("tweets", "id:300145260363931648", :wt => "json")["docs"].first.keys.sort
=> ["created_at", "entities_urls_display_url", "entities_urls_expanded_url", "entities_urls_indices", "entities_urls_url", "favorited", "id", "id_str", "metadata_iso_language_code", "metadata_result_type", "possibly_sensitive", "retweet_count", "retweeted", "source", "text", "truncated", "user_contributors_enabled", "user_created_at", "user_default_profile", "user_default_profile_image", "user_description", "user_entities_url_urls_indices", "user_entities_url_urls_url", "user_favourites_count", "user_followers_count", "user_friends_count", "user_geo_enabled", "user_id", "user_id_str", "user_is_translator", "user_lang", "user_listed_count", "user_location", "user_name", "user_profile_background_color", "user_profile_background_image_url", "user_profile_background_image_url_https", "user_profile_background_tile", "user_profile_banner_url", "user_profile_image_url", "user_profile_image_url_https", "user_profile_link_color", "user_profile_sidebar_border_color", "user_profile_sidebar_fill_color", "user_profile_text_color", "user_profile_use_background_image", "user_protected", "user_screen_name", "user_statuses_count", "user_time_zone", "user_url", "user_utc_offset", "user_verified"]

When you use MapReduce to collect the exact same doc the correct hierarchical structure of the hash is maintained:

[[{"metadata"=>{"result_type"=>"recent", "iso_language_code"=>"en"}, "created_at"=>"Sat Feb 09 07:33:01 +0000 2013", "id"=>300145260363931650, "id_str"=>"300145260363931648", "text"=>"Fractals in Clojure — Fractal Fern:\nhttp://t.co/u0k3iDBX", "source"=>"web", "truncated"=>false, "in_reply_to_status_id"=>nil, "in_reply_to_status_id_str"=>nil, "in_reply_to_user_id"=>nil, "in_reply_to_user_id_str"=>nil, "in_reply_to_screen_name"=>nil, "user"=>{"id"=>275148936, "id_str"=>"275148936", "name"=>"Juan Carlos Kuri", "screen_name"=>"jckuri", "location"=>"Guayaquil, Ecuador", "description"=>"I'm an artificial intelligence programmer, meta-programmer, mathematician, proactive singularitarian/transhumanist/extropian, extreme skater, and guitar player.", "url"=>"http://jckuri2005.comyr.com/", "entities"=>{"url"=>{"urls"=>[{"url"=>"http://jckuri2005.comyr.com/", "expanded_url"=>nil, "indices"=>[0, 28]}]}, "description"=>{"urls"=>[]}}, "protected"=>false, "followers_count"=>107, "friends_count"=>210, "listed_count"=>9, "created_at"=>"Thu Mar 31 18:34:08 +0000 2011", "favourites_count"=>31, "utc_offset"=>-21600, "time_zone"=>"Central Time (US & Canada)", "geo_enabled"=>false, "verified"=>false, "statuses_count"=>1929, "lang"=>"en", "contributors_enabled"=>false, "is_translator"=>false, "profile_background_color"=>"C0DEED", "profile_background_image_url"=>"http://a0.twimg.com/images/themes/theme1/bg.png", "profile_background_image_url_https"=>"https://si0.twimg.com/images/themes/theme1/bg.png", "profile_background_tile"=>false, "profile_image_url"=>"http://a0.twimg.com/profile_images/3101684315/54553879e8ab2585eadf470d53f7dd4f_normal.png", "profile_image_url_https"=>"https://si0.twimg.com/profile_images/3101684315/54553879e8ab2585eadf470d53f7dd4f_normal.png", "profile_banner_url"=>"https://si0.twimg.com/profile_banners/275148936/1352479045", "profile_link_color"=>"0084B4", "profile_sidebar_border_color"=>"C0DEED", "profile_sidebar_fill_color"=>"DDEEF6", "profile_text_color"=>"333333", "profile_use_background_image"=>true, "default_profile"=>true, "default_profile_image"=>false, "following"=>nil, "follow_request_sent"=>nil, "notifications"=>nil}, "geo"=>nil, "coordinates"=>nil, "place"=>nil, "contributors"=>nil, "retweet_count"=>0, "entities"=>{"hashtags"=>[], "urls"=>[{"url"=>"http://t.co/u0k3iDBX", "expanded_url"=>"http://nakkaya.com/2009/09/28/fractals-in-clojure-fractal-fern/", "display_url"=>"nakkaya.com/2009/09/28/fra…", "indices"=>[36, 56]}], "user_mentions"=>[]}, "favorited"=>false, "retweeted"=>false, "possibly_sensitive"=>false}]]

Here is what the top level keys for the MapReduce query looks like:

irb(main):032:0* Riak::MapReduce.new(client).search("tweets", "id:300145260363931648").map("Riak.mapValuesJson", :keep => true).run.flatten.first.keys.sort
=> ["contributors", "coordinates", "created_at", "entities", "favorited", "geo", "id", "id_str", "in_reply_to_screen_name", "in_reply_to_status_id", "in_reply_to_status_id_str", "in_reply_to_user_id", "in_reply_to_user_id_str", "metadata", "place", "possibly_sensitive", "retweet_count", "retweeted", "source", "text", "truncated", "user"]

As an example compare all user_* keys in the search() query with the nested user hash in the output of the MapReduce query.

Is this some feature for search() that I can turn off or change to ensure the data structures given back to me from Riak are consistent across search() and MapReduce?

Ruby client assumes MR results will always be arrays

Not sure if this qualifies as a bug in Riak KV or the Ruby client, but here is goes.

Riak MR is capable of returning Hashes. If you use a map function to bulk fetch values that returns both the key and the value as an array of tuples, Riak will convert the array of tuples into a JSON hash.

If you have a map phase function like:

map_object_key_value({error, notfound}, _, _) -> [];
map_object_key_value(RiakObject, _, _) ->
    [{ riak_object:key(RiakObject), riak_object:get_value(RiakObject) }].

You'll get back a JSON object from Riak, except when no input keys are found, in which case you get back an empty array.

This causes the Ruby client to blow up, as it expects the returned data to be an array.

Getting "Excon::Errors::SocketError: Connection reset by peer" frequently

This past week we upgraded riak from 0.14.2 to 1.0.3. We also upgraded our ripple client from 3595941bfc1615e2b10198bdcc8640074c47e560 (a commit from late July) to [1d109e9](https://github.com/seancribbs/ripple/commits/1d109e9c76bcac8376de02913a0798b4ea01c738 (a commit from Dec. 1st).

Since doing so, many of our resque jobs have been failing with:

Excon::Errors::SocketError
Connection reset by peer" frequently

The backtrace is below.

I think this may be related to a change I had to make due to API changes in riak client and ripple. Previously, we had a resque after_fork hook like so:

Resque.after_fork { |job| Ripple.client.backend.send(:connection).reset }

We had added this in the past after getting socket errors. It makes a lot of sense, really...since resque boots the app environment (which uses the excon connection at a few points) in the master process, and then forks off a child process for each job it processes--the connection could be stale when the child process starts. So we reset the socket to prevent this.

I can't do this anymore with the latest riak-client and ripple since the connection pool stuff takes a block now. Plus I thought the improved connection management stuff would take care of these errors.

Any ideas how I can improve this situation? Is there a new API I can use to ensure each resque job starts with a fresh connection?

/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/gems/excon-0.9.4/lib/excon/response.rb:21:in `parse'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/gems/excon-0.9.4/lib/excon/connection.rb:215:in `request_kernel'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/gems/excon-0.9.4/lib/excon/connection.rb:89:in `request'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/bundler/gems/ripple-1d109e9c76bc/riak-client/lib/riak/client/excon_backend.rb:57:in `perform'
/var/www/vhosts/silo/releases/20120121015546/lib/core_ext/riak_client_retryable.rb:12:in `perform'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/bundler/gems/ripple-1d109e9c76bc/riak-client/lib/riak/client/http_backend/transport_methods.rb:44:in `get'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/bundler/gems/ripple-1d109e9c76bc/riak-client/lib/riak/client/http_backend/configuration.rb:149:in `block in server_config'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/bundler/gems/ripple-1d109e9c76bc/riak-client/lib/riak/client/http_backend/configuration.rb:147:in `tap'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/bundler/gems/ripple-1d109e9c76bc/riak-client/lib/riak/client/http_backend/configuration.rb:147:in `server_config'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/bundler/gems/ripple-1d109e9c76bc/riak-client/lib/riak/client/http_backend/configuration.rb:161:in `riak_kv_wm_buckets'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/bundler/gems/ripple-1d109e9c76bc/riak-client/lib/riak/client/http_backend/configuration.rb:157:in `new_scheme?'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/bundler/gems/ripple-1d109e9c76bc/riak-client/lib/riak/client/http_backend/configuration.rb:67:in `object_path'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/bundler/gems/ripple-1d109e9c76bc/riak-client/lib/riak/client/http_backend.rb:67:in `fetch_object'
/var/www/vhosts/silo/releases/20120121015546/lib/core_ext/riak_checksum.rb:25:in `fetch_object'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/bundler/gems/ripple-1d109e9c76bc/riak-client/lib/riak/client.rb:275:in `block in get_object'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/bundler/gems/ripple-1d109e9c76bc/riak-client/lib/riak/client.rb:428:in `block in recover_from'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/bundler/gems/ripple-1d109e9c76bc/riak-client/lib/riak/client/pool.rb:126:in `take'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/bundler/gems/ripple-1d109e9c76bc/riak-client/lib/riak/client.rb:426:in `recover_from'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/bundler/gems/ripple-1d109e9c76bc/riak-client/lib/riak/client.rb:281:in `http'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/bundler/gems/ripple-1d109e9c76bc/riak-client/lib/riak/client.rb:126:in `backend'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/bundler/gems/ripple-1d109e9c76bc/riak-client/lib/riak/client.rb:274:in `get_object'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/bundler/gems/ripple-1d109e9c76bc/riak-client/lib/riak/bucket.rb:88:in `get'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/bundler/gems/ripple-1d109e9c76bc/ripple/lib/ripple/document/finders.rb:113:in `find_one'
/var/www/vhosts/silo/shared/bundle/ruby/1.9.1/bundler/gems/ripple-1d109e9c76bc/ripple/lib/ripple/document/finders.rb:55:in `find'

RObject#url doesn't know about load-balancing

Moved from https://github.com/seancribbs/ripple/issues/249 by @johnae:

Below method in RObject to get the url doesn't know about load-balancing.

# Generates a URL representing the object according to the client, bucket and key.
# If the key is blank, the bucket URL will be returned (where the object will be
# submitted to when stored).
def url
  segments = [ @bucket.client.http_paths[:prefix], escape(@bucket.name)]
  segments << escape(@key) if @key
  @bucket.client.http.path(*segments).to_s
end

ProtobuffsFailedRequest Unknown message code

Hello. I have a problem with searching keys in riak. Not sure, that's is bug. Let's see example:

client = Riak::Client.new(:protocol => "pbc")
client.search("name:Amadi")

return next error:

Riak::ProtobuffsFailedRequest: Expected success from Riak but received 0. Unknown message code.
    from /home/amadi/.rvm/gems/ruby-1.9.3-p194/gems/riak-client-1.1.1/lib/riak/client/beefcake_protobuffs_backend.rb:182:in `decode_response'
    from /home/amadi/.rvm/gems/ruby-1.9.3-p194/gems/riak-client-1.1.1/lib/riak/client/beefcake_protobuffs_backend.rb:152:in `search'
    from /home/amadi/.rvm/gems/ruby-1.9.3-p194/gems/riak-client-1.1.1/lib/riak/client/search.rb:24:in `block in search'
    from /home/amadi/.rvm/gems/ruby-1.9.3-p194/gems/riak-client-1.1.1/lib/riak/client.rb:435:in `block in recover_from'
    from /home/amadi/.rvm/gems/ruby-1.9.3-p194/gems/innertube-1.0.2/lib/innertube.rb:127:in `take'
    from /home/amadi/.rvm/gems/ruby-1.9.3-p194/gems/riak-client-1.1.1/lib/riak/client.rb:433:in `recover_from'
    from /home/amadi/.rvm/gems/ruby-1.9.3-p194/gems/riak-client-1.1.1/lib/riak/client.rb:379:in `protobuffs'
    from /home/amadi/.rvm/gems/ruby-1.9.3-p194/gems/riak-client-1.1.1/lib/riak/client.rb:133:in `backend'
    from /home/amadi/.rvm/gems/ruby-1.9.3-p194/gems/riak-client-1.1.1/lib/riak/client/search.rb:23:in `search'
    from (irb):23
    from /home/amadi/.rvm/rubies/ruby-1.9.3-p194/bin/irb:16:in `<main>'

That's "status: 0" is expected, as I have seen somewhere in the search response, and its status was 0.

Recommend Projects

  • React photo React

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

  • Vue.js photo Vue.js

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

  • Typescript photo Typescript

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

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

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

Recommend Topics

  • javascript

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

  • web

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

  • server

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

  • Machine learning

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

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

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

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.