Provisioning Cluster Resources

    +
    Provisioning cluster resources is managed at the collection or bucket level, depending upon the service affected. Common use cases are outlined here, less common use cases are covered in the API docs.

    Unresolved include directive in modules/howtos/pages/provisioning-cluster-resources.adoc - include::7.1.2@sdk:shared:partial$flush-info-pars.adoc[]

    The Java SDK also comes with some convenience functionality for common Couchbase management requests.

    Management operations in the SDK may be performed through several interfaces depending on the object:

    When using a Couchbase version earlier than 6.5, you must create a valid Bucket connection using Cluster.bucket(name) before you can use cluster level managers.

    Bucket Management

    The BucketManager interface may be used to create and delete buckets from the Couchbase cluster. It is instantiated through the Cluster.buckets() method.

    Cluster cluster = Cluster.connect("localhost", "Administrator", "password");
    BucketManager bucketMgr = cluster.buckets();

    The BucketSettings object is used for creating or updating buckets, and for exposing information about existing buckets.

    Unresolved include directive in modules/howtos/pages/provisioning-cluster-resources.adoc - include::7.1.2@sdk:shared:partial$flush-info-pars.adoc[]

    Here is the list of parameters available:

    Name

    Description

    Can be updated

    name string

    The name of the bucket, required for creation.

    false

    flushEnabled boolean

    Enables flushing to be performed on this bucket (see the Flushing Buckets section below).

    true

    replicaIndexes boolean

    Whether or not to replicate indexes.

    false

    ramQuotaMB uint64

    How much memory should each node use for the bucket, required for creation.

    true

    numReplicas int

    The number of replicas to use for the bucket.

    true

    bucketType BucketType

    The type of the bucket, required for creation.

    false

    evictionPolicy EvictionPolicyType

    The type of the eviction to use for the bucket, defaults to VALUE_ONLY.

    true (note: changing will cause the bucket to restart causing temporary inaccessibility)

    maxExpiry time.Duration

    The default maximum time-to-live to apply to documents in the bucket. (note: This option is only available for Couchbase and Ephemeral buckets in Couchbase Enterprise Edition.)

    true

    compressionMode CompressionMode

    The compression mode to apply to documents in the bucket. (note: This option is only available for Couchbase and Ephemeral buckets in Couchbase Enterprise Edition.)

    true

    conflictResolutionType ConflictResolutionType

    The conflict resolution type to apply to conflicts on the bucket, defaults to SEQUENCE_NUMBER

    false

    The following example creates a "hello" bucket:

    try {
    	BucketSettings bucketSettings = BucketSettings.create("hello")
    			.flushEnabled(false)
    			.replicaIndexes(true)
    			.ramQuotaMB(150)
    			.numReplicas(1)
    			.bucketType(BucketType.COUCHBASE)
    			.conflictResolutionType(ConflictResolutionType.SEQUENCE_NUMBER);
    
    	bucketMgr.createBucket(bucketSettings);
    } catch (BucketExistsException e) {
    	System.out.println("Bucket already exists");
    }

    We can now get this bucket and update it to enable Flush:

    BucketSettings settings = bucketMgr.getBucket("hello");
    settings.flushEnabled(true);
    
    bucketMgr.updateBucket(settings);

    Once you no longer need to use the bucket, you can remove it:

    bucketMgr.dropBucket("hello");

    Flushing Buckets

    Unresolved include directive in modules/howtos/pages/provisioning-cluster-resources.adoc - include::7.1.2@sdk:shared:partial$flush-info-pars.adoc[]

    You can flush a bucket in the SDK by using the flushBucket method:

    bucketMgr.flushBucket("hello");

    The Flush operation may fail if the bucket does not have flush enabled, in that case it will return a BucketNotFlushableException.

    Collection Management

    The CollectionManager interface may be used to create and delete scopes and collections from the Couchbase cluster. It is instantiated through the Bucket.collections() method. Refer to the CollectionManager API documentation — and to its Async counterpart — for further details.

    Cluster cluster = Cluster.connect("localhost", "scopeAdmin", "password");
    Bucket bucket = cluster.bucket("travel-sample");
    CollectionManager collectionMgr = bucket.collections();

    You can create a scope:

    try {
      collectionMgr.createScope("example-scope");
    }
    catch (ScopeExistsException e) {
      System.out.println("Scope already exists");
    }

    You can then create a collection within that scope:

    CollectionSpec spec = CollectionSpec.create("example-collection", "example-scope");
    
    try {
      collectionMgr.createCollection(spec);
    }
    catch (CollectionExistsException e) {
      System.out.println("Collection already exists");
    }
    catch (ScopeNotFoundException e) {
      System.out.println("The specified parent scope doesn't exist");
    }

    Finally, you can drop unneeded collections and scopes:

    try {
      collectionMgr.dropCollection(spec);
    }
    catch (CollectionNotFoundException e) {
      System.out.println("The specified collection doesn't exist");
    }
    catch (ScopeNotFoundException e) {
      System.out.println("The specified parent scope doesn't exist");
    }
    
    try {
      collectionMgr.dropScope("example-scope");
    }
    catch (ScopeNotFoundException e) {
      System.out.println("The specified scope doesn't exist");
    }

    Note that the most minimal permissions to create and drop a Scope or Collections is Manage Scopes along with Data Reader

    You can create users with the appropriate RBAC programmatically:

    users.upsertUser(
      new User("scopeAdmin")
      .password("password")
      .displayName("Manage Scopes [travel-sample:*]")
      .roles(
        new Role("scope_admin", "travel-sample", "*", "*"),
        new Role("data_reader", "travel-sample", "*", "*")));

    Index Management

    Unresolved include directive in modules/howtos/pages/provisioning-cluster-resources.adoc - include::7.1.2@sdk:shared:partial$flush-info-pars.adoc[]

    QueryIndexManager

    The QueryIndexManager interface contains the means for managing indexes used for queries. It can be instantiated through the Cluster.queryIndexes() method.

    Cluster cluster = Cluster.connect("localhost", "Administrator", "password");
    QueryIndexManager queryIndexMgr = cluster.queryIndexes();

    Unresolved include directive in modules/howtos/pages/provisioning-cluster-resources.adoc - include::7.1.2@sdk:shared:partial$flush-info-pars.adoc[]

    The example below shows how to create a simple primary index, restricted to a named scope and collection, by calling the createPrimaryIndex() method. Note that you cannot provide a named scope or collection separately, both must be set for the QueryIndexManager to create an index on the relevant keyspace path.

    Creating a primary index
    CreatePrimaryQueryIndexOptions opts = CreatePrimaryQueryIndexOptions.createPrimaryQueryIndexOptions()
    		.scopeName("tenant_agent_01")
    		.collectionName("users")
    		// Set this if you wish to use a custom name
    		// .indexName("custom_name") 
    		.ignoreIfExists(true);
    
    queryIndexMgr.createPrimaryIndex("travel-sample", opts);

    When a primary index name is not specified, the SDK will create the index as #primary by default. However, if you wish to provide a custom name, you can simply set an indexName property in the CreatePrimaryQueryIndexOptions object.

    You may have noticed that the example also sets the ignoreIfExists boolean flag. When set to true, this optional argument ensures that an exception is not thrown if an index under the same name already exists.

    Creating a secondary index follows a similar approach, with some minor differences:

    Creating a secondary index
    try {
    	CreateQueryIndexOptions opts = CreateQueryIndexOptions.createQueryIndexOptions()
    			.scopeName("tenant_agent_01")
    			.collectionName("users");
    
    	queryIndexMgr.createIndex("travel-sample", "tenant_agent_01_users_email",
    			Arrays.asList("preferred_email"), opts);
    } catch (IndexExistsException e) {
    	System.out.println("Index already exists");
    }

    The createIndex() method requires an index name to be provided, along with the fields to create the index on. Like the primary index, you can restrict a secondary index to a named scope and collection by passing some options.

    Indexes can easily take a long time to build if they contain a lot of documents. In these situations, it is more ideal to build indexes in the background. To achieve this we can use the deferred boolean option, and set it to true.

    Deferring index creation
    try {
    	// Create a deferred index
    	CreateQueryIndexOptions createOpts = CreateQueryIndexOptions.createQueryIndexOptions()
    			.scopeName("tenant_agent_01")
    			.collectionName("users")
    			.deferred(true);
    
    	queryIndexMgr.createIndex("travel-sample", "tenant_agent_01_users_phone",
    			Arrays.asList("preferred_phone"), createOpts);
    
    	// Build any deferred indexes within `travel-sample`.tenant_agent_01.users
    	BuildQueryIndexOptions deferredOpts = BuildQueryIndexOptions.buildDeferredQueryIndexesOptions()
    			.scopeName("tenant_agent_01")
    			.collectionName("users");
    
    	queryIndexMgr.buildDeferredIndexes("travel-sample", deferredOpts);
    
    	// Wait for indexes to come online
    	WatchQueryIndexesOptions watchOpts = WatchQueryIndexesOptions.watchQueryIndexesOptions()
    			.scopeName("tenant_agent_01")
    			.collectionName("users");
    
    	queryIndexMgr.watchIndexes("travel-sample", Arrays.asList("tenant_agent_01_users_phone"), 
    			Duration.ofSeconds(60), watchOpts);
    
    } catch (IndexExistsException e) {
    	System.out.println("Index already exists");
    }

    To delete a query index you can use the dropIndex() or dropPrimaryIndex() methods. Which one you use depends on the type of query index you wish to drop from the database.

    Deleting an index
    DropPrimaryQueryIndexOptions primaryIndexOpts = DropPrimaryQueryIndexOptions.dropPrimaryQueryIndexOptions()
    		.scopeName("tenant_agent_01")
    		.collectionName("users");	
    
    queryIndexMgr.dropPrimaryIndex("travel-sample", primaryIndexOpts);
    
    // Drop a secondary index
    DropQueryIndexOptions indexOpts = DropQueryIndexOptions.dropQueryIndexOptions()
    		.scopeName("tenant_agent_01")
    		.collectionName("users");	
    
    queryIndexMgr.dropIndex("travel-sample", "tenant_agent_01_users_email", indexOpts);

    Views Management

    Unresolved include directive in modules/howtos/pages/provisioning-cluster-resources.adoc - include::7.1.2@sdk:shared:partial$flush-info-pars.adoc[]

    In the SDK, design documents are represented by the DesignDocument and View objects. All operations on design documents are performed on the ViewIndexManager instance:

    Cluster cluster = Cluster.connect("localhost", "Administrator", "password");
    Bucket bucket = cluster.bucket("travel-sample");
    ViewIndexManager viewMgr = bucket.viewIndexes();

    The following example upserts a design document with two views:

    Map<String, View> views = new HashMap<>();
    views.put(
    	"by_country",
    	new View("function (doc, meta) { if (doc.type == 'landmark') { emit([doc.country, doc.city], null); } }")
    );
    views.put(
    	"by_activity",
    	new View(
    		"function (doc, meta) { if (doc.type == 'landmark') { emit([doc.country, doc.city], null); } }",
    		"_count")
    );
    
    DesignDocument designDocument = new DesignDocument("landmarks", views);
    viewMgr.upsertDesignDocument(designDocument, DesignDocumentNamespace.DEVELOPMENT);

    Unresolved include directive in modules/howtos/pages/provisioning-cluster-resources.adoc - include::7.1.2@sdk:shared:partial$flush-info-pars.adoc[]

    Note the use of DesignDocumentNamespace.DEVELOPMENT, the other option is DesignDocumentNamespace.PRODUCTION. This parameter specifies whether the design document should be created as development, or as production — with the former running over only a small fraction of the documents.

    Now that we’ve created a design document we can fetch it:

    DesignDocument designDocument = viewMgr.getDesignDocument("landmarks", DesignDocumentNamespace.DEVELOPMENT);
    System.out.print(designDocument);

    We’ve created the design document using DesignDocumentNamespace.DEVELOPMENT and now want to push it to production, we can do this with:

    viewMgr.publishDesignDocument("landmarks");

    To remove this design document:

    viewMgr.dropDesignDocument("landmarks", DesignDocumentNamespace.PRODUCTION);