Git Product home page Git Product logo

riak-java-client's Introduction

Riak Java Client

Maven Central

The Riak Java Client enables communication with Riak, an open source, distributed database that focuses on high availability, horizontal scalability, and predictable latency. Both Riak and this code are maintained by Basho.

The latest version of the Java client supports both Riak KV 2.0+, and Riak TS 1.0+. Please see the Release Notes for more information on specific feature and version support.

  1. Installation
  2. Documentation
  3. Contributing
  4. Roadmap
  5. License and Authors
  6. 2.0 Overview

Installation

This branch of the Riak Java Client is for the new v2.0 client, to be used with Riak 2.0.

Previous versions:

riak-client-1.4.4 - For use with Riak 1.4.x

riak-client-1.1.4 - For use with < Riak 1.4.0

This client is published to Maven Central and can be included in your project by adding:

<dependencies>
  <dependency>
    <groupId>com.basho.riak</groupId>
    <artifactId>riak-client</artifactId>
    <version>2.0.7</version>
  </dependency>
  ...
</dependencies>

All-in-one jar builds are available here for those that don't want to set up a maven project.

Documentation

  • Develop: Build Status

Most documentation is living in the wiki. For specifics on our progress here, see the release notes.

Also see the Javadoc site for more in-depth API docs.

Contributing

Integration Tests Setup

We've included Basho's riak-client-tools as a git submodule to help with Riak test setup and teardown. You can find these tools in the /tools subdirectory.

To configure your single riak instance, you may use the setup-riak script to setup the node with the appropriate bucket types. You can use it by running ./tools/setup-riak.

To configure a devrel for multiple node testing, please see the instructions located at basho/riak-client-tools on how to use the ./tools/setup-riak command with your devrel.

Running Integration Tests

To run the Riak KV integration test suite, execute:

make RIAK_PORT=8087 integration-test

To run the Riak TimeSeries test suite, execute:

make RIAK_PORT=8087 integration-test-timeseries

When running tests directly from Maven, you may also turn feature sets on and off with system properties:

mvn -Pitest,default -Dcom.basho.riak.timeseries=true -Dcom.basho.riak.pbcport=$(RIAK_PORT) verify

The supported test flags are:

System Property Default Value Note
com.basho.riak.buckettype true Riak KV 2.0 Bucket Type Tests
com.basho.riak.yokozuna true Riak KV 2.0 Solr/Yokozuna Search Tests
com.basho.riak.2i true Riak KV Secondary Index Tests
com.basho.riak.mr true Riak KV MapReduce Tests
com.basho.riak.crdt true Riak KV 2.0 Data Type Tests
com.basho.riak.lifecycle true Java Client Node/Cluster Lifecycle Tests
com.basho.riak.timeseries false Riak TS TimeSeries Tests
com.basho.riak.riakSearch false Riak KV 1.0 Legacy Search Tests
com.basho.riak.coveragePlan false Riak KV/TS Coverage Plan Tests
(need cluster to run these )
com.basho.riak.security false Riak Security Tests
com.basho.riak.clientcert false Riak Security Tests with Certificates

To run the HyperLogLog or GSet Data Type tests, you must have two test bucket types setup as following:

riak-admin bucket-type create gsets '{"props":{"allow_mult":true, "datatype": "gset"}}'
riak-admin bucket-type create hlls '{"props":{"allow_mult":true, "datatype": "hll"}}'
riak-admin bucket-type activate gsets
riak-admin bucket-type activate hlls

Some tests may require more than one feature to run, so please check the test to see which ones are required before running.

Connection Options

System Property Default Value Note
com.basho.riak.host 127.0.0.1 The hostname to connect to for tests
com.basho.riak.pbcport 8087 The Protocol Buffers port to connect to for tests

Security tests

To run the security-related integration tests, you will need to:

  1. Setup the certs by running the buildbot makefile's configure-security-certs target.
cd buildbot;
make configure-security-certs;
cd ../;
  1. Copy the certs to your Riak's etc dir, and configure the riak.conf file to use them.
resources_dir=./src/test/resources
riak_etc_dir=/fill/in/this/path/

# Shell
cp $resources_dir/cacert.pem $riak_etc_dir
cp $resources_dir/riak-test-cert.pem $riak_etc_dir
cp $resources_dir/riakuser-client-cert.pem $riak_etc_dir

# riak.conf file additions
ssl.certfile = (riak_etc_dir)/cert.pem
ssl.keyfile = (riak_etc_dir)/key.pem
ssl.cacertfile = (riak_etc_dir)/cacert.pem
  1. Enable Riak Security.
riak-admin security enable
  1. Create a user "riakuser" with the password "riak_cert_user" and configure it with certificate as a source
riak-admin security add-user riakuser
riak-admin security add-source riakuser 0.0.0.0/0 certificate
  1. Create a user "riak_trust_user" with the password "riak_trust_user" and configure it with trust as a source.
riak-admin security add-user riak_trust_user password=riak_trust_user
riak-admin security add-source riak_trust_user 0.0.0.0/0 trust
  1. Create a user "riakpass" with the password "riak_passwd_user" and configure it with password as a source.
riak-admin security add-user riakpass password=Test1234
riak-admin security add-source riakpass 0.0.0.0/0 password
  1. Run integration-test-security target of the makefile.
make integration-test-security

This repository's maintainers are engineers at Basho and we welcome your contribution to the project! Review the details in CONTRIBUTING.md in order to give back to this project.

An honest disclaimer

Due to our obsession with stability and our rich ecosystem of users, community updates on this repo may take a little longer to review.

The most helpful way to contribute is by reporting your experience through issues. Issues may not be updated while we review internally, but they're still incredibly appreciated.

Thank you for being part of the community!

Roadmap

TODO

License and Authors

The Riak Java Client is Open Source software released under the Apache 2.0 License. Please see the LICENSE file for full license details.

Contributors

Thank you to all of our contributors! If your name is missing please let us know.

2.0 Overview

Version 2.0 of the Riak Java client is a completely new codebase. It relies on Netty4 in the core for handling network operations and all operations can be executed synchronously or asynchronously.

Getting started with the 2.0 client

The new client is designed to model a Riak cluster:

RJC model

The easiest way to get started with the client is using one of the static methods provided to instantiate and start the client:

RiakClient client =
    RiakClient.newClient("192.168.1.1","192.168.1.2","192.168.1.3");

The RiakClient object is thread safe and may be shared across multiple threads.

For more complex configurations, you can instantiate a RiakCluster from the core packages and supply it to the RiakClient constructor.

RiakNode.Builder builder = new RiakNode.Builder();
builder.withMinConnections(10);
builder.withMaxConnections(50);

List<String> addresses = new LinkedList<String>();
addresses.add("192.168.1.1");
addresses.add("192.168.1.2");
addresses.add("192.168.1.3");

List<RiakNode> nodes = RiakNode.Builder.buildNodes(builder, addresses);
RiakCluster cluster = new RiakCluster.Builder(nodes).build();
cluster.start();
RiakClient client = new RiakClient(cluster)

Once you have a client, commands from the com.basho.riak.client.api.commands.* packages are built then executed by the client.

Some basic examples of building and executing these commands is shown below.

Getting Data In

Namespace ns = new Namespace("default", "my_bucket");
Location location = new Location(ns, "my_key");
RiakObject riakObject = new RiakObject();
riakObject.setValue(BinaryValue.create("my_value"));
StoreValue store = new StoreValue.Builder(riakObject)
  .withLocation(location)
  .withOption(Option.W, new Quorum(3)).build();
client.execute(store);

Getting Data Out

Namespace ns = new Namespace("default","my_bucket");
Location location = new Location(ns, "my_key");
FetchValue fv = new FetchValue.Builder(location).build();
FetchValue.Response response = client.execute(fv);
RiakObject obj = response.getValue(RiakObject.class);

Using 2.0 Data Types

A bucket type must be created (in all local and remote clusters) before 2.0 data types can be used. In the example below, it is assumed that the type "my_map_type" has been created and associated to the "my_map_bucket" prior to this code executing.

Once a bucket has been associated with a type, all values stored in that bucket must belong to that data type.

Namespace ns = new Namespace("my_map_type", "my_map_bucket");
Location location = new Location(ns, "my_key");
RegisterUpdate ru1 = new RegisterUpdate(BinaryValue.create("map_value_1"));
RegisterUpdate ru2 = new RegisterUpdate(BinaryValue.create("map_value_2"));
MapUpdate mu = new MapUpdate();
mu.update("map_key_1", ru1);
mu.update("map_key_2", ru2);
UpdateMap update = new UpdateMap.Builder(location, mu).build();
client.execute(update);

RiakCommand Subclasses

Fetching, storing and deleting objects

Listing keys in a namespace

Secondary index (2i) commands

Fetching and storing datatypes (CRDTs)

Querying and modifying buckets

Search commands

Map-Reduce

riak-java-client's People

Contributors

aleksandrpavlenko avatar alexmoore avatar argv0 avatar beerriot avatar broach avatar bryanhuntesl avatar christopherfrieler avatar codahale avatar coderoshi avatar dizzyd avatar empovit avatar jbrisbin avatar jplock avatar lukebakken avatar mgodave avatar michaelklishin avatar oleksii-suprun avatar paulbarry avatar pcl avatar randysecrist avatar russelldb avatar seancribbs avatar srgg avatar taichi avatar theicemancometh avatar tnm avatar tolsi avatar vikua avatar vivek-balakrishnan-rovio avatar zkhadikova avatar

Stargazers

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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  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-java-client's Issues

Question - Automatic nodes discovery

When I initialize a PBClusterConfig is it mandatory to add every single node in my cluster or I can just add one and it automatically discovers all the others?

Thanks

Content type stored as Constants/CTYPE_JSON_UTF8 is read as a different string

In Welle we have a "smart" deserialization feature that works like this: if IRiakObject content type is known, we deserialize the value automatically for common formats like text or JSON. During this process, IRiakObject's content type is compared with the Constants/CTYPE_JSON_UTF8 value. In 1.1 this value is

public static String CTYPE_JSON_UTF8 = "application/json;charset=UTF-8";

note that there is no space between base content type and parameters. This is not a problem in general but when content type is read from IRiakObject, the space between base type and parameter list magically appears and comparison fails: application/json;charset=UTF-8" is not equal to application/json; charset=UTF-8".

This is a very annoying problem that makes the constant rather useless.

Check for proper escaping of "/" in Java client

Moved from https://issues.basho.com/show_bug.cgi?id=244, reported by @argv0

Comment from @krestenkrab:

One problem is, that it is not easy to figure out what the client should do.

In general, we need to support any binary as bucket/key, so should we require

BaseUrl ++ url_encode(Bucket, utf8) ++ "/" ++ url_encode(Key, utf8) 

Where Bucket/Key are "binaries", i.e. characters/bytes in the range 0..255.

or what? The encoding of these URL's is not documented anywhere as far as I can see?

At least all bytes in the range 0..255 are valid utf8 code points, so if Bucket and Key are just lists of such bytes, then this should work.

Load balancing in ClusterClient suboptimal in multithreaded applications.

The current load balancing in the ClusterClient is a very naive round-robin. Every API call is simply given the next connection in the list and if a node is down the next one in the list is then used in a retry. No connection is every removed from the list, either temporarily or permanently. A synchronized index variable is used to determine which connection to use.

In a multi-threaded application a case can occur where an API call fails even when there are plenty of valid connections. This is due to thread interleaving causing a single thread to retry on the same "bad" node until the number of retries is exhausted.

Client only returning 1 of N links when they have the same bucket/key

I've been testing out the link/link-walking feature and noticed that if I have a value with multiple links, with the same bucket/key, the client will only return one of those even tho there are multiple with varying tags.

For example, say I have the following links:

{"bucket" : "users", "key" : "john", "tag" : "geek"}
{"bucket" : "users", "key" : "john", "tag" : "family"}
{"bucket" : "users", "key" : "mary", "tag" : "family"}

You'll notice that the key john is in there twice. The Java client only returns one link for john. If I perform a GET with curl I can verify that all three links are in-fact stored.

PB client uses MR for 2i

The old legacy code at the bottom of the client uses Map/Reduce for 2i queries. This is less than optimal. We have since added support in Riak for 2i queries via PB and need to change the client to use that rather than MR.

No request timeout option in PB client

When using protocol buffers there's no way to set a request timeout. The old underlying PB client uses the original Java IO so setting the SO_TIMEOUT on the Socket to a user-supplied value is the way to fix this.

Riak client ignoring error codes

Hi,

I found an error related to this one in the server: https://issues.basho.com/show_bug.cgi?id=1324

The client receives a 400 but does not seem to treat it correctly. The problem seems to lie on the method:

public FetchResponse(HttpResponse r, RiakClient riak) throws RiakResponseRuntimeException, RiakIORuntimeException

which treats 300 code, successful codes, but does nothing in case of error codes (500, 400 which is my specific cas). In my example as you can read in the test the server is returning a 400 (which is not correct, it comes from a server bug, but should be detected for the client, not hidden).

The behavior I would expect (maybe I am wrong ;) ) is to get a RiakException when that happens instead of a null IRiakObject.

My fix suggestion is throwing a RiakException if the code is null in FetchResponse but I don't know the code in depth so there might be a better place to do it.

Thanks a lot and keep on working like that!

Deadlock during shutdown in container - pools

I'm using riak client from a thread inside Tomcat. Pool is created for a client from a PBClusterConfig
Tomcat fails to shut down gracefully with deadlock because the pools don't shut down.

I've made a fork that fixes it for me, but not sure if it's best way to solve. No easy way to get access to pools from high level client, so I added a shutdown method on client interface that shuts down the pools for cluster configs, and is NOP otherwise.
If you don't call shutdown() behaviour is exactly as before

  public void run()
  {
      PBClientConfig node1 = new PBClientConfig.Builder().withHost("n1").withPort(8087).build();
      PBClusterConfig tempConf = new PBClusterConfig(16);
      tempConf.addClient(node1);

      if(true){
        PBClientConfig node2 = PBClientConfig.Builder.from(node1).withHost("n2").withPort(8087).build();
        tempConf.addClient(node2);
      }

      IRiakClient c=RiakFactory.newClient(tempConf);

      while(running){
        sleep(1000);
        c.ping();
....
      }
      c.shutdown();

I didn't push changes to the fork yet to understand if this acceptable way to solve

logical filter is not working

Hi,

The logical filter is not working for me and I couldn't find any example on this. Please find below my code,

final KeyFilter[] filters = new KeyFilter[] {new EndsWithFilter("2"),new EndsWithFilter("1")};

LogicalOrFilter laf = new LogicalOrFilter(filters);

MapReduceResult result = riakClient.mapReduce("customer")
.addKeyFilter(laf)
.addMapPhase(new NamedJSFunction("Riak.mapValuesJson"), false)
.addReducePhase(new NamedErlangFunction("riak_kv_mapreduce",
"reduce_sort"), true) .execute();

IRiakClient missing bucket delete operation, by design?

Based on the docs, my impression was that the preferred method of using a client was via RiakFactory. This hands back an IRiakClient instance, on which list, fetch and update are operations that can be performed on a bucket. However, deleting a bucket is not an exposed operation. And, even if casting to DefaultRiakClient, the underlying RawClient cannot be retrieved for bucket deletions.

Should the iRiakClient expose the delete operation? If not, it appears the only way to go about it is to manually create (thus duplicating configuration setup) the underlying raw client and then delete the bucket. Just wondered if I was missing something here...

Thanks for the help!

-Russ-

Support For Erlang ETF for MapReduce Queries

Right now, the only way to get binary values via Map/Reduce is through ETF. The only client that currently supports this right now is the Erlang client. Using JInterface, it should be possible to serialize/deserialize ETF.

Java HTTP client performance abysmal

Moved from https://issues.basho.com/show_bug.cgi?id=486, reported by @seancribbs

Java client with this code takes a very long time to insert documents: http://pastebin.com/D0LCZeud In contrast, a shell script with curl inserts much more quickly: http://pastebin.com/3sjrGR05 The Java version should be comparable or more performant than invoking curl from CLI.

(Verified that stats on the Riak side were fine: http://pastebin.com/Edp62PAh)
(From Infin1ty on IRC)

Relevant IRC logs:

http://irclogger.com/riak/2010-07-21
http://irclogger.com/riak/2010-07-22

Blocking in ClientHelper.executeMethod() on record deletion

I have a series of integration tests which, as as consequence of the code being executed, are adding and deleting records from Riak via the Java client.

I have a single test which adds a couple of objects to different buckets, then deletes the objects after the test. For some reason, this one particular test - which is only different to the other tests by virtue of utilising more than one bucket - is causing ClientHelper.executeMethod() to consistently block on object deletion.

Specifically, the following line blocks:

org.apache.http.HttpResponse response =  httpClient.execute(httpMethod);

after invoking:

bucket.delete(key).execute();

Invoking the corresponding deletion via CURL on the command-line works fine and deletes the object immediately. As an experiment, invoking the following naive and hacky code in my test consistently also works fine:

HttpClient client = new DefaultHttpClient();
HttpRequestBase request = new HttpDelete("http://127.0.0.1:8091/riak/" + bucketName + "/" + key);
client.execute(request);

The amount of data being added to Riak during the tests is pretty minimal - two or three records with a few fields at most.

Curiously, if I remove the JUnit @After annotation from the cleanup method (which, FWIW, iterates through all buckets, then all keys in the bucket, deleting them) and instead invoke the method via a @Before, it also consistently works fine.

I'm afraid I don't know enough about HttpClient to offer any further insight, and I don't (yet) have a repeatable test case that won't involve sharing my entire project. If you'd like me to whip something up, let me know. I know that this is all a bit straw-graspy, but hopefully something will leap out (thread pool configuration in ClientUtils.newHttpClient()?)

bug in method com.basho.riak.client.raw.pbc.PBClientConfig.from()

The method com.basho.riak.client.raw.pbc.PBClientConfig.from() return an "empty" PBClientConfig.Builder. It should return a PBClientConfig.Builder build from the parameter provided to the method.

Line 204 should be changed from

return new PBClientConfig.Builder();

to

return b;

But maybe the method is not very relevant. As the method did not work I instead did like this (which i think is more simple)

PBClientConfig.Builder defaultBuilder = new PBClientConfig.Builder().with.......

PBClientConfig node1 = defaultBuilder.withHost("riak01").build();
PBClientConfig node2 = defaultBuilder.withHost("riak02").build();
PBClientConfig node3 = defaultBuilder.withHost("riak03").build();
PBClientConfig node4 = defaultBuilder.withHost("riak05").build();

Annotations on super classes are ignored

The AnnotationScanner class doesn't scan parent classes and therefore any annotations declared on those classes are ignored. The proper behavior should be to recursively walk up the inheritance hierarchy and scan for annotations on each class until we reach the java.lang.Object parent class.

For annotations that aren't additive (e.g. @RiakKey), the behavior should be that an annotation declared on a child class wins (i.e. overrides any such annotation on a parent class).

I have a fix for this issue and I'll be creating a pull request shortly.

Add constructor to JSONConverter<T> that accepts a Jackson ObjectMapper instance

ObjectMapper creation is expensive and for Jackson 2.x the Joda module needs to be added explicitly, so domain POJO with Joda DateTime fails unless you get the exposed ObjectMapper from the JSONConverter<T> and add the Joda module to it which is kind of hacky, applications might use a single ObjectMapper instance for all.

According to Jackson best practices, if there isn't any particular behavior; you could reuse the ObjectMapper; source: http://wiki.fasterxml.com/JacksonBestPracticesPerformance

HTTP client configuration is challenging when using cluster clients

When a maximum connection count is provided, HTTPClusterClient.fromConfig() disregards any provided HttpClient objects, and instead creates a new DefaultHttpClient for each cluster member. This, combined with the tight encapsulation of the object returned from RiakFactory.newClient(), seems to make it pretty darned hard to do any Riak-specific HTTP client configuration.

In particular, I want to disable staleness checks, as they cost a fair bit, both in terms of latency and CPU load. And since Riak already retries for me, I'd just as soon be optimistic.

In the interim, I dig in via reflection (blech) and set the parameter after client creation:

private void reconfigureHttpClient(IRiakClient client) {
    try {
        DefaultRiakClient defaultRiakClient = (DefaultRiakClient) client;
        HTTPClusterClient rawClient = (HTTPClusterClient) getField(defaultRiakClient, null, "rawClient");
        RawClient[] cluster = (RawClient[]) getField(rawClient, null, "cluster");
        for (RawClient rawClientClusterMember : cluster) {
            HTTPClientAdapter httpClientAdapter = (HTTPClientAdapter) rawClientClusterMember;
            RiakClient riakClientClusterMember = (RiakClient) getField(httpClientAdapter, null, "client");
            DefaultHttpClient httpClient = (DefaultHttpClient) riakClientClusterMember.getHttpClient();
            httpClient.getParams().setParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false);
        }
    } catch (NoSuchFieldException | IllegalAccessException e) {
        log.warn("Couldn't reconfigure Riak HTTP client! This means stale connection checks will not be disabled.", e);
    }
}

private Object getField(Object o, Class cls, String fieldName) throws IllegalAccessException, NoSuchFieldException {
    if (cls == null)
        cls = o.getClass();
    try {
        Field field = cls.getDeclaredField(fieldName);
        field.setAccessible(true);
        return field.get(o);
    } catch (NoSuchFieldException e) {
        if (cls.getSuperclass() != Object.class)
            return getField(o, cls.getSuperclass(), fieldName);
        else
            throw e;
    }
}

SyntaxError when map-reducing after delete

After I delete an object and the run a map-reduce, I get this error:

java.io.IOException: {&quot;phase&quot;:0,&quot;error&quot;:&quot;[{&lt;&lt;\&quot;lineno\&quot;&gt;&gt;,466},{&lt;&lt;\&quot;message\&quot;&gt;&gt;,&lt;&lt;\&quot;SyntaxError: syntax error\&quot;&gt;&gt;},{&lt;&lt;\&quot;source\&quot;&gt;&gt;,&lt;&lt;\&quot;()\&quot;&gt;&gt;}]&quot;,&quot;input&quot;:&quot;{ok,{r_object,&lt;&lt;\&quot;my-bucket-name\&quot;&gt;&gt;,&lt;&lt;\&quot;test2\&quot;&gt;&gt;,[{r_content,{dict,4,16,16,8,80,48,{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},{{[],[],[],[],[],[],[],[],[],[],[[&lt;&lt;\&quot;X-Riak-VTag\&quot;&gt;&gt;,54,101,109,74,98,55,114,115,83,73,57,76,79,57,71,69,97,79,103,84,74,97]],[[&lt;&lt;\&quot;index\&quot;&gt;&gt;]],[[&lt;&lt;\&quot;X-Riak-Deleted\&quot;&gt;&gt;,116,114,117,101]],[[&lt;&lt;\&quot;X-Riak-Last-Modified\&quot;&gt;&gt;|{1358,935280,816043}]],[],[]}}},&lt;&lt;&gt;&gt;}],[{&lt;&lt;244,107,211,192,80,210,203,74&gt;&gt;,{5,63526154610}},{&lt;&lt;2,204,117,50,80,210,203,56&gt;&gt;,{5,63526154599}},{&lt;&lt;117,...&gt;&gt;,...}],...},...}&quot;}
    com.basho.riak.client.raw.pbc.PBClientAdapter.mapReduce(PBClientAdapter.java:416)
    com.basho.riak.client.query.MapReduce.execute(MapReduce.java:78)

What's up here?

The map-reduce is targeting the Solr API.

Interceptor feature request

Hi,

It would be great to have support for the concept of interceptors, much like Hibernate's Interceptor class, or JPA's PrePersist etc. annotations. This would allow the developer to register pre-persist, post-persist, pre-delete, etc. hooks that allow for non-Riak functionality to be executed when explicit Riak operations are taking place.

For example, we are using Riak in order to provide metadata around externally-managed files on disk. When metadata is deleted, the files should be deleted. With the registration of a deletion interceptor against the Riak-persisted POJO, this means that we can be absolutely certain that there will be consistency between the filesystem and what's in Riak.

M

A Mutation<T> should have a criteria to determine if the target Object was mutated, avoiding unnecessary server round trip.

A _Mutation < T >_ should have a way of returning false when the fetched Object from Riak contained already that mutation avoiding a server round trip, basically, _hasMutated()_ should return true by default unless the developer overrides such method to return true or false base on some criteria.

_Example:_

We have a bucket for daily changes, so each daily change has a list of IDs and the category (So there is a key per day and category), lets say I modify an ID and when registering that ID into the corresponding daily change, it was already on that list because it has been already modified that day, so my list of entries on that daily change will have the same size after adding such Integer (let's say a Set < Integer >) rendering _hasMutated() = false_

Then Riak java client should not save it back to Riak, but if the developer decides to not to use such functionality, the default should be _hasMutated() = true_

2i integer indexes limited to Integer.MAX_VALUE

The addIndex() method in the IRiakObject takes an int as its second argument. This limits the value to Integer.MAX_VALUE (2^31-1). Riak/erlang do not have this limitation nor does the HTTP or Protocol Buffers API.

Round robin connection pool to all hosts in a cluster

I made this patch to have the RiakConnectionPool connect to all specified hosts of a cluster in a round robin manner.

Please review. Thanks.


--- ../riak-java-client/src/main/java/com/basho/riak/pbc/RiakConnectionPool.java    2012-05-24 13:24:15.000000000 -0700
+++ src/main/java/com/basho/riak/pbc/RiakConnectionPool.java    2012-05-24 13:23:02.000000000 -0700
@@ -129,8 +129,9 @@
      */
     public static final int LIMITLESS = 0;
     private static final int CONNECTION_ACQUIRE_ATTEMPTS = 3;
-    private final InetAddress host;
-    private final int port;
+    private final InetAddress hosts[];
+    private final int ports[];
+    private int currentAddressIndex;
     private final Semaphore permits;
     private final ConcurrentLinkedQueue<RiakConnection> available;
     private final ConcurrentLinkedQueue<RiakConnection> inUse;
@@ -170,7 +171,9 @@
      */
     public RiakConnectionPool(int initialSize, int maxSize, InetAddress host, int port,
             long connectionWaitTimeoutMillis, int bufferSizeKb, long idleConnectionTTLMillis) throws IOException {
-        this(initialSize, getSemaphore(maxSize), host, port, connectionWaitTimeoutMillis, bufferSizeKb,
+        this(initialSize, getSemaphore(maxSize),
+            new InetAddress[] { host }, new int[] { port },
+            connectionWaitTimeoutMillis, bufferSizeKb,
              idleConnectionTTLMillis);

         if (initialSize > maxSize && (maxSize > 0)) {
@@ -205,12 +208,43 @@
      */
     public RiakConnectionPool(int initialSize, Semaphore poolSemaphore, InetAddress host, int port,
             long connectionWaitTimeoutMillis, int bufferSizeKb, long idleConnectionTTLMillis) throws IOException {
+       this(initialSize, poolSemaphore, new InetAddress[] { host }, new int[] { port },
+            connectionWaitTimeoutMillis, bufferSizeKb, idleConnectionTTLMillis);
+    }
+
+    /**
+     * Crate a new host connection pool. NOTE: before using you must call
+     * start()
+     * 
+     * @param initialSize
+     *            the number of connections to create at pool creation time
+     * @param clusterSemaphore
+     *            a {@link Semaphore} set with the number of permits for the
+     *            pool (and maybe cluster (see {@link PoolSemaphore}))
+     * @param hosts
+     *            all the hosts this pool holds connections to
+     * @param ports
+     *            all corresponding ports on those hosts
+     * @param connectionWaitTimeoutMillis
+     *            the connection timeout
+     * @param bufferSizeKb
+     *            the size of the socket/stream read/write buffers (3 buffers,
+     *            each of this size)
+     * @param idleConnectionTTLMillis
+     *            How long for an idle connection to exist before it is reaped,
+     *            0 mean forever
+     * @throws IOException
+     *             If the initial connection creation throws an IOException
+     */
+    public RiakConnectionPool(int initialSize, Semaphore poolSemaphore, InetAddress hosts[], int ports[],
+            long connectionWaitTimeoutMillis, int bufferSizeKb, long idleConnectionTTLMillis) throws IOException {
         this.permits = poolSemaphore;
         this.available = new ConcurrentLinkedQueue<RiakConnection>();
         this.inUse = new ConcurrentLinkedQueue<RiakConnection>();
         this.bufferSizeKb = bufferSizeKb;
-        this.host = host;
-        this.port = port;
+        this.hosts = hosts;
+        this.ports = ports;
+        this.currentAddressIndex = 0;
         this.connectionWaitTimeoutNanos = TimeUnit.NANOSECONDS.convert(connectionWaitTimeoutMillis, TimeUnit.MILLISECONDS);
         this.initialSize = initialSize;
         this.idleConnectionTTLNanos = TimeUnit.NANOSECONDS.convert(idleConnectionTTLMillis, TimeUnit.MILLISECONDS);
@@ -276,13 +310,42 @@
     }

     /**
+     * Connect to the next host. If, after exhaustively trying all hosts,
+     * connection cannot be made, throw an IOException.
+     * 
+     * @param timeOutMillis
+     *            the timeout value (in milliseconds)
+     * @return a new RiakConnection
+     * @throws IOException
+     */
+    public RiakConnection doCreateConnection(long timeOutMillis) throws IOException {
+       RiakConnection conn = null;
+       for (int i = 1; conn == null && i <= this.hosts.length; i++) {
+           int idx = (this.currentAddressIndex + i) % this.hosts.length;
+           try {
+               conn = new RiakConnection(this.hosts[idx],
+                       this.ports[idx], this.bufferSizeKb, this,
+                       timeOutMillis);
+           } catch (IOException e) {
+               continue;
+           }
+           this.currentAddressIndex = idx;
+       }
+       if (conn == null) {
+           throw new IOException("cannot connect to any host");
+       }
+       return conn;
+    }
+    
+    /**
      * If there are any initial connections to create, do it.
      * @throws IOException
      */
     private void warmUp() throws IOException {
         if (permits.tryAcquire(initialSize)) {
             for (int i = 0; i < this.initialSize; i++) {
-                available.add(new RiakConnection(this.host, this.port, this.bufferSizeKb, this));
+                available.add(doCreateConnection(TimeUnit.MILLISECONDS.convert(
+                       connectionWaitTimeoutNanos, TimeUnit.NANOSECONDS)));
             }
         } else {
             throw new RuntimeException("Unable to create initial connections");
@@ -375,7 +438,7 @@
         try {
             if (permits.tryAcquire(connectionWaitTimeoutNanos, TimeUnit.NANOSECONDS)) {
                 try {
-                    return new RiakConnection(host, port, bufferSizeKb, this, TimeUnit.MILLISECONDS.convert(connectionWaitTimeoutNanos, TimeUnit.NANOSECONDS));
+                    return doCreateConnection(TimeUnit.MILLISECONDS.convert(connectionWaitTimeoutNanos, TimeUnit.NANOSECONDS));
                 } catch(SocketTimeoutException e) {
                     throw new AcquireConnectionTimeoutException("timeout from socket connection " + e.getMessage());
                 } catch (IOException e) {

Use SLF4J (particularly instead of printStackTrace)

This is a suggestion that adds a small dependency. There are a few cases throughout the code where printStackTrace() is called and quite a few others where some (tunable) logging would be a great addition.

SLF4J (depending on the API only and importing slf4j-simple for testing) would allow these messages to be put into place in a way that worked with whatever logger the client was using already, without having to worry about things going to stderr.

Unexhausted KeySource in PB client returns connection to pool with data still in buffer

When listing keys using protocol buffers if you do not read all the keys from the Iterable (KeySource) it is possible for the RiakConnection to be returned to the RiakConnectionPool with data sill on the wire, subsequently be used for another Riak operation, then receive an IOException because when the client attempts to read the response for the new operation it gets a RpbListKeysResp

Can't detect deleted siblings

When rapidly writing and deleting keys, sometimes tombstones appear in the list of siblings. Using the FetchObject operation and adding a resolver, it is not directly possible to detect whether a given sibling is a tombstone because the X-Riak-Deleted HTTP header / PBC deleted field are not exposed at the IRiakObject level. Instead, you have to check for an empty value (0 length on getValue) or an empty content-type.

RawClient API methods that return void could return something more useful

While working on Welle, I have stumbled upon an interesting test failure with deleted objects. Someone suggested it may be worth inspecting HTTP headers returned by Riak but RawClient API method that I used (#delete) returns void. So do some other methods, like some overrides of #store, while others return RiakResponse instances.

I see no reason to not return HttpResponse or RiakResponse for operations like #delete. API clients that don't need them will ignore them. With void/nil returned, there is no easy way to get meaningful response information when you need it.

MapReduce doesn't find key if value for that key is a byte array with any of it's byte < 0

Hello

I'm not sure it's a client problem, but this simple test fails:

import com.basho.riak.client.IRiakClient;
import com.basho.riak.client.RiakFactory;
import com.basho.riak.client.bucket.Bucket;
import com.basho.riak.client.query.MapReduceResult;
import com.basho.riak.client.query.filter.EqualToFilter;
import com.basho.riak.client.query.filter.LogicalAndFilter;
import com.basho.riak.client.query.filter.LogicalFilterGroup;
import com.basho.riak.client.query.filter.TokenizeFilter;
import com.basho.riak.client.query.functions.JSSourceFunction;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

import java.util.Arrays;
import java.util.Collection;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;

public class RiakBinaryTest
    extends Assert
{
    protected static IRiakClient client = null;

    @BeforeClass
    public static void beforeClass()
        throws Exception
    {
        if (client == null)
        {
            client = RiakFactory.httpClient(
                "http://localhost:4444/riak");
        }
    }

    @AfterClass
    public static void afterClass()
        throws Exception
    {
        if (client != null)
        {
            client.shutdown();
        }
    }

    @Test
    public void test_mapred_problem()
        throws Exception
    {
        final Bucket bucket =
            client
                .createBucket("my-bucket")
                .execute();

        // Test passes OK using this value
        // final byte[] value = new byte[]{0, 1, 2};

        // Test fails using this value
        final byte[] value = new byte[]{-1, 0, 1};

        bucket
            .store("x1|x2|x3", value)
            .execute();
        bucket
            .store("y1|x2|x3", value)
            .execute();

        final byte[] expected =
            bucket
                .fetch("x1|x2|x3")
                .execute()
                .getValue();

        assertTrue("Value is not set",
                   Arrays.equals(value, expected));

        final MapReduceResult result =
            client
                .mapReduce(bucket.getName())
                .addKeyFilter(
                    new LogicalAndFilter(
                        new LogicalFilterGroup(
                            new TokenizeFilter("|", 1),
                            new EqualToFilter("x1")),
                        new LogicalFilterGroup(
                            new TokenizeFilter("|", 2),
                            new EqualToFilter("x2")),
                        new LogicalFilterGroup(
                            new TokenizeFilter("|", 3),
                            new EqualToFilter("x3"))))
                .addMapPhase(
                    new JSSourceFunction(
                        "function(v) { return [v.key]; }"))
                .execute();

        final Collection<String> keys =
            result
                .getResult(String.class);

        assertNotNull(keys);
        assertThat("Invalid Result count",
                   keys.size(), is(equalTo(1)));
    }
}

I'd really appreciate if this weird behavior can be explained.

Thanks,
-- Roman

Fields annotated with @RiakKey, @RiakUsermeta included in JSON output

Moved from https://issues.basho.com/show_bug.cgi?id=1338, reported by David Connelly

Using Riak Java client version 1.0.4 + Jackson 1.9.2

I've assumed that Riak's ObjectMapper should exclude @RiakKey and @RiakUsermeta annotated fields from JSON serialization output, but it looks like this is not the case.

Stepping into RiakBeanSerializerModifier (~ line 83) shows that AnnotatedField does not include all the declared annotations on the field. Perhaps this is some sort of Jackson bug?

Very easy to reproduce by running test cases with log4j.logger.org.apache.http.wire=DEBUG enabled. I can also attach a sample program if necessary.

FYI, a workaround is to also annotate the @RiakKey field with @JsonIgnore.

ConflictResolver in DomainBucket get an empty list

Hi

When I use a DomainBucket with a conflict resolver to create new items the conflict resolver gets an empty list. This makes it very hard to resolve any conflict.

To me this looks like a bug, but I might just have missed something, so I'll add more detailed info if it is not something obvious. I'm using the 1.0.5 client.

Add Cookbook pointer to README

I did a quick scan for a pointer to the cookbook in the README but couldn't find it. We should probably add one as people might not always see the Wiki tab.

PBC requires httpclient library

The PBC code uses DateUtils and DateParseException from the apache http library. Would it be better to avoid using the http libraries if you're not actually using http?

Error in delete commands with parameters

Hi,

I was experiencing some weird problem with the Java client and I think I discovered the reason. The method:

static com.basho.riak.client.raw.http.ConversionUtil.RequestMeta convert(DeleteMeta deleteMeta);

seems to have a bug. This is the code:

static RequestMeta convert(DeleteMeta deleteMeta) {
    RequestMeta rm = convert(new FetchMeta(deleteMeta.getR(), deleteMeta.getPr(), null, null, null, null, null,
                                           null));

    if (deleteMeta.hasW()) {
        rm.setQueryParam(Constants.QP_W, deleteMeta.getW().toString());
    }

    // More stuff like that,...

    // Here it returns null, but I guess it should return rm, because if not, it does not matter which parameters you specified, they will never be given to the Riak server.
    return null;
}

Fix is very easy I guess.

Head request for checking key existence

For records with large payload, it's infeasible to check if the result of the fetch is null. From an HTTP client a HEAD instead of a GET does the trick. Can this be implemented?

Allow store operations with null key

As Riak offers the possibility to store values without a key an leave key generation up to Riak it would be great if this could be used through the riak-java-client.

No way to store a previously fetched object without another fetch

The current client design is centered around a specific usage pattern where data modification is done inside a store operation via a Mutation object. (See: https://github.com/basho/riak-java-client/wiki/Storing-data-in-riak#wiki-advanced ).

This is limiting in that if you have already fetched data from Riak then decide you want to modify and store it ... you can't. Furthermore the current design does not provide a way to only store an object back to Riak if it has been modified. If the logic inside the Mutation does not result in the object being mutated it is still stored back to Riak needlessly.

JsonMappingException when sys_global_heaps_size=deprecated

When calling DefaultRiakClient#stats(), a JsonMappingException occurs when sys_global_heaps_size is equal to the string deprecated. I'm using the driver version 1.0.7 and Riak build from the master with erlang version R15B03.

org.codehaus.jackson.map.JsonMappingException: Can not construct instance of java.math.BigInteger from String value 'deprecated': not a valid representation
 at [Source: java.io.StringReader@4ae0a5dc; line: 1, column: 1540] (through reference chain: com.basho.riak.client.query.NodeStats["sys_global_heaps_size"])

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.