5

I am trying to connect to two different buckets in couchbase using spring boot. But in a single spring boot application the database config only takes a single bucket name.

Is it possible to connect to more than one couchbase bucket in spring-boot?

piyush
  • 115
  • 2
  • 12

3 Answers3

4

So it seems you want to use Spring Data Couchbase from within a Spring Boot application, and have (at least) two different repositories backed by two different Bucket?

You'll have to customize your Spring Data configuration programmatically (as opposed to letting Spring Boot do all the heavy lifting), but that's possible.

  • Spring Boot creates a CouchbaseConfigurer through which it creates default Cluster and Bucket (as tuned in the properties file).
  • If you have a CouchbaseRepository on your classpath, it'll also attempt to configure Spring Data by instantiating a SpringBootCouchbaseDataConfiguration class.
  • You can customize that by extending the SpringBootCouchbaseDataConfiguration above in your project, marking it as @Configuration

Once you're ready to customize the Spring Data configuration programmatically, what you need is to create a second Bucket bean, a second CouchbaseTemplate that uses that bucket, and then instruct Spring Data Couchbase on which template to use with which Repository.

To that end, there is a configureRepositoryOperationsMapping(...) method. You can use the parameter of this method as a builder to:

  • link a specific Repository interface to a CouchbaseTemplate: map
  • say that any repo with a specific entity type should use a given template: mapEntity
  • even redefine the default template to use (initially the one created by Spring Boot): setDefault.

This second part is explained in the Spring Data Couchbase documentation.

Simon Baslé
  • 27,105
  • 5
  • 69
  • 70
  • Thanks, I am able to make connection to both the buckets. I have one doubt in this case. Will both these repositories have different connection or are they sharing the same connection? – piyush May 17 '16 at 19:48
  • 1
    As long as they use the same Cluster they will share as much resources as possible, including connections – Simon Baslé May 17 '16 at 19:56
  • Is there a way we can have different connections for both the repositories. One of the repositories is only for read operation and other one is for read/write operation. We are seeing a performance issue not sure is this is the case because of same connection. – piyush May 17 '16 at 20:21
  • We are seeing in couchbase taking upto 8ms to read and write the document in the bucket. Search is on the basis of key only. I have created a simple view. Apart from that i have not done anything assuming couchbase creates index for the keys automatically. – piyush May 17 '16 at 20:23
  • 1
    It is not currently easily supported by Spring Data Couchbase to have several clusters... But the sharing of resources, at the io level, is done by netty and it has not been a problem to work two buckets in parallel before, AFAIK. – Simon Baslé May 17 '16 at 20:28
  • @SimonBaslé dose this work with spring-data-couchbase 3.x.x and 'ReactiveCouchbaseRepository'? For me it seems to not add the extra bucket for the template and just uses the bucket for 'getBucketName()' and puts everyting in the default bucket – ASH Dec 11 '19 at 13:19
  • @ASH I haven't worked on the project since 2016, before the `ReactiveCouchbaseRepository` support was added, but it is very possible something was missing regarding multi-bucket support – Simon Baslé Dec 12 '19 at 14:13
1

Probably what you are trying to say is that Spring boot provides pre-defined properties that you can modify, such as: couchbase.cluster.bucket that takes single value and you want to connect to two or more buckets.

In case you will not find a better solution, I can point you to a slightly different approach, and that is to setup your own couchbase connection manager that you can inject anywhere you need.

Here is the example of such @Service that will provider you with two connections to different buckets.

You can modify to suite your needs, it is very small.

import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.couchbase.client.java.Bucket;
import com.couchbase.client.java.Cluster;
import com.couchbase.client.java.CouchbaseCluster;
import com.couchbase.client.java.env.CouchbaseEnvironment;
import com.couchbase.client.java.env.DefaultCouchbaseEnvironment;

@Service
public class CouchbaseConnectionManager {
    private static final int TIMEOUT = 100000;

    @Value("#{configProp['couchbase.nodes']}")
    private List<String> nodes = new ArrayList<String>();

    @Value("#{configProp['couchbase.binary.bucketname']}")
    private String binaryBucketName;

    @Value("#{configProp['couchbase.nonbinary.bucketname']}")
    private String nonbinaryBucketName;

    @Value("#{configProp['couchbase.password']}")
    private String password;

    private Bucket binaryBucket;

    private Bucket nonbinaryBucket;

    private Cluster cluster;

    private static final Logger log = Logger.getLogger(CouchbaseConnectionManager.class);

    @PostConstruct
    public void createSession() {

        if (nodes != null && nodes.size() != 0) {
            try {
                CouchbaseEnvironment env = DefaultCouchbaseEnvironment.builder().connectTimeout(TIMEOUT).build();

                cluster = CouchbaseCluster.create(env, nodes);

                binaryBucket = cluster.openBucket(binaryBucketName, password);
                nonbinaryBucket = cluster.openBucket(nonbinaryBucketName, password);
                log.info(GOT_A_CONNECTION_TO_COUCHBASE_BUCKETS + binaryBucket + " " + nonbinaryBucket);
            } catch (Exception e) {
                log.warn(UNABLE_TO_GET_CONNECTION_TO_COUCHBASE_BUCKETS);
            }
        } else {
            log.warn(COUCH_NOT_CONFIGURED);
        }
    }

    @PreDestroy
    public void preDestroy() {
        if (cluster != null) {
            cluster.disconnect();
            log.info(SUCCESSFULLY_DISCONNECTED_FROM_COUCHBASE);
        }
    }

    public Bucket getBinaryBucket() {
        return binaryBucket;
    }

    public Bucket getNonbinaryBucket() {
        return nonbinaryBucket;
    }

    private static final String SUCCESSFULLY_DISCONNECTED_FROM_COUCHBASE = "Successfully disconnected from couchbase";
    private static final String GOT_A_CONNECTION_TO_COUCHBASE_BUCKETS = "Got a connection to couchbase buckets: ";
    private static final String COUCH_NOT_CONFIGURED = "COUCH not configured!!";
    private static final String UNABLE_TO_GET_CONNECTION_TO_COUCHBASE_BUCKETS = "Unable to get connection to couchbase buckets";
}
Jenya G
  • 504
  • 3
  • 8
  • I am using Spring boot CouchbaseRepository to get the implementation of all curd operations in this case. I dont think i will be able to inject or spring boot will be able to give me curd operations implementation for both the buckets. – piyush Apr 29 '16 at 17:47
  • I am thinking of having different config class for each bucket and for each database config i can provide @EnableCouchbaseRepositories(basePackages = { "com.gp.api.common.repo.couchbase" }) so that it will identify the correct repo and instantiate the correct repo for each bucket with curd operations. – piyush Apr 29 '16 at 17:48
0

I followed Simon's approach and extended the org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration for the @Configuration instead of SpringBootCouchbaseDataConfiguration.

Also, a point worth mentioning is that for some reason having separate Repository packages and having its own @Configuration doesn't really work. I struggled a great deal to try and make it work and eventually settled on having all the Repositories in a single package and ended up having something like the below to map the Entities and Templates.

baseMapping.mapEntity(Prime.class, noSQLSearchDBTemplate())
                .mapEntity(PrimeDetailsMaster.class, noSQLSearchDBTemplate())
                .mapEntity(HostDetailsMaster.class, noSQLSearchDBTemplate())
                .mapEntity(Events.class, eventsTemplate())
                .mapEntity(EventRulesMaster.class, eventsTemplate());
ark202
  • 23
  • 10