Contents

Ortus Couchbase Extension

Ortus Couchbase Extension is a Lucee Extension that allows your CFML server to connect to a Couchbase cluster and leverage it for built-in caching, session storage, and NoSQL document storage.

Couchbase Server is a distributed NoSQL document database for interactive applications. Its scale-out architecture runs in the cloud or on commodity hardware and provides a flexible data model, consistent high-performance, easy scalability and always-on 24x365 availability.

Thanks to a flexible JSON model, Couchbase Server makes it easy to modify your applications without the constraints of a fixed database schema. Submillisecond, high-throughput reads and writes give you consistent high performance. Couchbase Server is easy to scale out, and supports topology changes with no downtime. Couchbase Inc.

 

Couchbase Web Dashboard showing bucket usage via Lucee Extension

Requirements

Features In A Nutshell

Installation

Couchbase Setup

You will need to install Couchbase Server separately. Your Couchbase cluster can be a single node, or multiple nodes. The nodes can be installed along side your web servers, or on a server farm of their own-- it's entirely up to you. The recommended setup would be to have at least 3 dedicated machines (even if virtual) with several Gigs of RAM availble to Couchbase.

3 Couchbase servers is the minimum required for auto-failover to work when employing a single replica configuration. You will need to determine the durability needs of your Couchbase setup or you can contact us at consulting@ortussolutions.com so we can help you build your Couchbase Farm.

Lucee Setup

The Couchbase extension is installed as a Lucee extension into any Lucee Server version 4.1.0.001 and above. The extension can either be installed in the server context or a specific web context.

Server Context



If installed in the server context, you can create server-level caches tha are available to all web contexts in Lucee . The driver will also be availble to all web contexts to add their own local caches or override caches if they need to.

Web Context



If the extension is installed in a specific web context, the Couchbase drivers will be available only to that context and not usable by any other web context on the server. Any other web contexts that want to use Couchbase would need to specifically install the extension as well. Be carefull installing multiple versions of the same extension in a single JVM as you can get overlapping versions of the same JAR files.

Recommended Setup

The recommended setup is to install the extension at the server level. If you have a cache that all contexts will share like session storage, you could create a cache in each web context that points to the same Couchbase bucket, but it is recommended to create that cache once at the server level and let all sites inherit it.

If you have a cache that is only going to be used for one site, it is recommended you create that cache in the web context for that site only.

Important: You can create multiple named Lucee caches that point to the same Couchbase bucket. To reduce confusion, try not to do this unless you have a good reason. A reason could be that you want one application to have a different operation timeouts.

Add Extension Provider

In your Lucee Administrator under Extension > Providers paste in your Ortus provider URL (http:// Lucee .ortussolutions.com/ExtensionProvider.cfc) and click save.

Adding Extension Provider

One added, the new provider URL should show up in the list as verified.

Ortus Extension Provider Added

Installing the Extension

Now click Extensions > Applications and wait for the list to load. There should be two new items in the list called Ortus Couchbase Cache and Ortus Couchbase Cache-trial. You will be installing the non-trial version since you have purchased the extension.

Extension in the list of not installed items

Click it and then click the install button to begin the installation and activation process.

Extension details

You will now see our EULA and license agreement, just press agree to continue:

Extension EULA & License

Activating the Extension

You will now see our activation screen where you will have to enter the license key and license email with which you purchased the extension with. You will also have to determine the type of server you are installing the extension to. Development or non-public facing servers are FREE of charge and by default receive up to 4 activations. Production servers get only 1 activation, so make sure you choose the right server type. Once you get all your information in the form then click on the install button to finalize the installation.

Note : Development and staging servers are FREE of charge and each license includes up to 4 activations. Production licenses are on a per Lucee instance and are allowed 1 activation. If you have any activation issues please contact us at support@ortussolutions.com. Also, make sure you havea valid internet connection in order to activate your product.

Extension Activation

The Ortus Lucee Couchbase Extension should now be installed on your server and ready to use. We highly recommend you restart your entire servlet container (Tomcat/Resin/jetty/etc) in order for all Jar files to take effect. Make sure your Couchbase cluster is running and proceed on to the next step-- creating a cache.

Important: Please note that the Couchbase Extension is licensed on a per-JVM basis. You will need a license for each separate JVM, regardless of how many contexts (sites) are deployed in your Lucee installation. The typical setup is one JVM per physical/virtual server. Please ask us if you have licensing questions.

Configuration

If you install the Extension in the Server Administrator, you will be able to create server-level caches as well as site-specific caches in each of the Web Administrators. If you install the extension in a web administrator, you will only be able to create Couchbase caches for that site. We recommend installing the extension in the Server Administrator so it is available to all the websites under that same Lucee installation.

To add a new cache, click on Services > Cache and you should see a list of existing caches. If there are no existing caches, you should be taken straight to the create screen. Type a name for this cache connection and choose Couchbase Server from the type dropdown and click create.

Create a new cache

You will then be taken to a page of options for the Couchbase Cache implementation. Check the Storage box if you want to be able to use this cache for session or client storage distribution. Enter the Couchbase server IP addresses or host names in the text area one per line. If you have many servers in your Couchbase cluster, you only need to list a few of them here. The client library will do auto discovery of the rest of the nodes once it connects. Enter your bucket name and password.

Important: If you are using the default bucket, there will be no password required.

The Default dropdown allows you to specify this connection to be used as the default storage mechanism for function, object, template, query, or resource caches in Lucee . If you set this at the server level, it can be overridden at the website level. Click Submit to finish creating the cache connection and save its settings.

Cache settings

Here's an overview of all the settings available to you on a cache-by-cache basis:

Setting Type Default Description
Storage yes/no no Allow to use this cache as client/session storage.
Couchbase Server Hosts Text area localhost:8091 Couchbase server host and port. Put each server and port on a new line.
Couchbase vBucket Name Text default Couchbase vBucket to connect to.
Bucket password Text --- Password for this bucket. Blank if using "default" bucket.
Operation Queue Max Block Time Text 10000 The maximum time to block waiting for op queue operations to complete, in milliseconds.
Operation Timeout Text 2500 Time in millisecs for an operation to Timeout.
Timeout Exception Threshold Text 998 Number of operations to timeout before the node is deemed down.
Default Dropdown --- Define the default cache connection for Function, Object, Template, Query, or Resource caches.

Usage

There are a number of ways that the extension integrates Couchbase into your Lucee server. There is built-in caching, manual get/set of objects, and scope storage.

CFML

The first and easiest way to use your new cache is via the built-in cache functions and tags in CFML. You can specify the cache name to use, but by default Lucee will use the cache you have designated as your default object cache.

Caching Objects

If you have selected your cache as the default Object cache in the admin, you can simply use functions like cachePut() and cacheGet(). Couchbase can handle arbitrary snippets of text (like HTML snippets) and even complex objects such as queries, arrays, or structs. The cache will automatically serialize the values and store them as text. They will be reconstituted when you retreive them from the cache later.

Important: A single cache item can be up to 20 MB in size!

If you pass in complex objects to cachePut(), they will be serialized as binary and not usable in Couchbase views.

// Set a simple value into the default "object" cache for 2 days
cachePut( "key", "value", 2 );
// Get that value back
cacheGet( "key" );

// Set a value into a specific cache for 1 day
cachePut( "key", "value", 1, "myCacheName" );
// Get that value back
cacheGet( "key", "myCacheName" );

// Cache this array for 3 hours
cachePut( "myArray", [1,2,3,4,5], createTimeSpan(0,3,0,0) );

Caching Function Output

Once you have selected a cache as the default function cache in the admin, you can use Couchbase to cache the results of oft-run functions. The cache key will be created for you automatically based on a hash of the method arguments thanks to Lucee . Cached functions should therefore be deterministic-- meaning the output of the function is purely a product of its parameters. Using function caching is easy, just add a cachedwithin attribute to the cffunction tag or function declaration.

<---  Cache results of this function for 5 minutes --->
<cffunction name="myFunc" cachedwithin="#createTimeSpan(0,0,5,0)#">
	<cfargument name="inputText">
	
	<cfreturn reverse(inputText)>
</cffunction>

<---  First execution stores result in cache --->
<cfset result = myFunc("Brad Wood")>

<---  subsequent call skips execution and pulls results from cache --->
<cfset result = myFunc("Brad Wood")>

<---  Different parameters will create a new cache entry --->
<cfset result = myFunc("Luis Majano")>

This is what that function would look like in script

function myFunc( inputText ) cachedwithin="#createTimeSpan(0,0,5,0)#" {
	return reverse(inputText);
}

Important: Lucee only supports caching functions that accept simple values as paramters. Therefore, a function that accepts an array would not be cacheable.

Caching Queries

Once you have selected a cache as the default query cache in the admin, you can use Couchbase to cache the results of oft-run database queries. The cache key will be created for you automatically based on a hash of the SQL statement and its parameters thanks to Lucee . Using query caching is as easy as function caching, just add a

attribute to your query.

<---  Cache results of this query for 15 minutes --->
<cfquery datasource="datasourceName" name="qryTest" cachedwithin="#createTimeSpan(0,0,15,0)#">
	select *
	from [tableName]
</cfquery>

Lucee RAM Resources

Lucee  has the concept and feature of virtual file systems.  Our extension taps into it by allowing you to leverage the ram:// resource to talk to Couchbase for storing documents and treating Couchbase like a big file system.  This means that any file or directory related tag/function can work with Couchbase RAM resource like: fileRead(), fileWrite(), fileDelete(), directoryNew(), directoryList(), include, etc. It even works when defining application mappings.  You can define Couchbase to be the default cache for resources by selecting the default element in the cache details page and pointing it to resource.

Important: Please note that Lucee only allows you to determine 1 cache connection to be the default for either object, template, query, resource or function caching. So you might end up creating different cache connections if you want to leverage them as default cache locations.

Once you setup Couchbase to be your default resource cache you can leverage it in many ways:

// Create a mapping to it
this.mappings[ "/cluster-fs" ] = "ram://";


// Do some file operations
fileWrite( "/cluster-fs/suzi.txt", myContent );
include template="/cluster-fs/aop.cfm";
directoryList( "/cluster-fs/caches" );

Working With JSON Documents

Couchbase is more than a simple key/value store though; it is also a NoSQL document store. If you pass in a string that can be parsed as JSON, will be treated specially and indexed as JSON inside Couchbase. You will be able to create views in Couchbase that reference specific keys in your JSON document if you wish. We have created some methods to help you interact with Couhbase as a document store. However, we do recommend that you use our CFCouchbase Companion SDK for all your NoSQL needs.

couchbaseSet
Parameter Required Type Default Description
id Yes string --- This is the ID of the document to set.
value Yes any --- The value to be stored. If the value is complex, it will be serialized as JSON. If JSON serialization is not possible, it will be stored as binary.
serializeQueryByColumns No boolean false This flag is used to control how queries are serialized. See docs for serializeJSON().
cacheName No string --- Specify the name of the Lucee cache for the delete operation to be exeucted on. If none is specified, the Lucee default Object cache is used.

// Set an array
CouchbaseSet( "myArray", ['a','b','c','d','e'] );

// Set a struct
CouchbaseSet( "myStruct", {"foo":"bar","loo":5,"subStruct":{"goo":"smar"},"subArray":[1,2,3,4,5]}, "cacheName" );
couchbaseGet
Parameter Required Type Default Description
id Yes string --- This is the ID of the document to set.
throwWhenNotExist No boolean No Set this to true to throw an error if the ID being retrieved doesn't exist. Default is false. If false, null will be returned.
cacheName No string --- Specify the name of the Lucee cache for the delete operation to be exeucted on. If none is specified, the Lucee default Object cache is used.

CouchbaseGet( "myArray" );

CouchbaseGet( "myStruct", "cacheName" );
couchbaseDelete
Parameter Required Type Default Description
id Yes string --- This is the ID of the document to set.
throwOnError No boolean No Set this to true to throw an error if the ID being deleted doesn't exist. Default is false.
cacheName No string --- Specify the name of the Lucee cache for the delete operation to be exeucted on. If none is specified, the Lucee default Object cache is used.

CouchbaseDelete( "myArray" );

CouchbaseDelete( "myStruct", "cacheName" );

Executing Couchbase Queries

Let's pretend we have a bucket in Couchbase called "employee" where we stored a number of documents representing the workers at our company. In the Couchbase admin we can create an indexed view that returns employee name as the key. We won't go into how to create views here. You can read about it in the Couchbase docs.

Let's assume our view that returns employees by name is called "employeeName" and exists in a design document called "employee_views". We can execute that view directly from our CFML code with a special built-in function (BIF) that the extension registers with Lucee .

// Return all employees, sorted by name.
results = couchbaseQueryView("employee_views", "employeeName");

// Return all employees named "Brad" and include the full JSON document
results = couchbaseQueryView(designDocument="employee_views", viewName="employeeName", key="Brad", includeDoc=true);

By default this function will return an array of structs representing the id, key, and value of matching items in the view. There are optional parameters to return the entire document for each matching result as well as return the native Java objects for you to interate over. Here's a rundown of the parameters the couchbaseQueryView accepts.

couchbaseQueryView
Parameter Required Type Default Description
designDocument Yes string --- Name of the design document this view belongs to.
viewName Yes string --- Name of the Couchbase view to query.
includeDoc No boolean false Specifies whether or not to include the entire document in the results or just the key names. If returnArray is false, this setting will return a ViewResponse obejct when true and a ViewResponseNoDocs object when false
returnArray No boolean true Specifies whether or not to return a Java ViewResponse/ViewResponseNoDocs object or ColdFusion array.
stale No string "OK" Specifies if stale data can be returned with the view. Possible values are
  • "OK" - stale data is acceptable
  • "FALSE" - force index of view
  • "UPDATE_AFTER" - potentially return stale data, but starts an asynch re-index.
sortDirection No string ASC Specifies the direction to sort the results based on the map function's "key" value. Valid values are ASC and DESC.
offset No number --- Number of records to skip when returning
limit No number --- Number of records to return
key No any --- The key of a single record to return. Also will accept an array of keys to return.
startKey No any --- Specify the start of a range of keys to return. This value needs to be the same data type as the key in your view's map function. If your view has a string for the key, pass in a string here. If your key is an array of values, pass in an array of values.
endKey No any --- Specify the end of a range of keys to return. This value needs to be the same data type as the key in your view's map function. If your view has a string for the key, pass in a string here. If your key is an array of values, pass in an array of values.
startKeyDocID No string --- If you have specified a startKey AND there is more than one record in the view results that share that key, this will specify what ID to start at when returning records. This input is ignored when not using startKey.
endKeyDocID No string --- If you have specified an endKey AND there is more than one record in the view results that share that key, this will specify what ID to end at when returning records. This input is ignored when not using endKey.
includeEndKey No boolean No Use this when specifying an endKey parameter. Flag to control whether the endKey is inclusive or not.
reduce No boolean Yes Flag to control whether the reduce portion of the view is run. If false, only the results of the map function are returned.
group No boolean No Flag to control whether the results of the reduce function are grouped. If no groupLevel is specified, only one row will be returned.
groupLevel No number --- Number representing what level of the map key to group at (Keys can be complex). If the key is simple, this parameter does nothing.
cacheName No string --- Definition of the cache used by name, when not set the "default Object Cache" defined in Lucee Administrator is used instead.

Per-application Cache Settings

You can speicify what caches are the default storage mechanism for functions, queries, objects, etc in the Lucee server and web administrator. There is one more programmatic level this can be configured as well.

The following settings are available in your Application.cfc to override at an application level. Remember, the "object" cache is used by default if no cache name is specified to functions such as cachePut(), cacheGet(), couchbaseSet(), and couchbaseGet().

component{
	this.cache.function="<cache-name>";
	this.cache.query="<cache-name>";
	this.cache.object="<cache-name>";
	this.cache.resource="<cache-name>";
	this.cache.template="<cache-name>";
}

Scope storage

Lucee  will allow you to seamlessly defer storage of your session and/or client scopes to Couchbase.  You will set and access the scopes like you normally would in CFML, but behind the scenes Lucee stores a cache entry for each user containing their session/client variables in the Couchbase cluster.  This serves several purposes:


This means you could take your single-server application with a heavy dependence on session/client variables, and scale it out to as many web servers as neccessary behind a round-robin load balancer. Users are free to roam to any web server they want, and their session variables will follow them anywhere with no extra ovearhead.

Or perhaps you have Millions of web site visitors and you're running out of heap space due to all the session storage. Push those sessions off to a distributed Couchbase layer that can scale out to meet demand, and that is no longer a bottleneck.

For both session and client storage, you will need to check the Storage box when creating or editing the cache connection you want to use in the Lucee admin. Remember you can create multuple buckets in Couchbase to help manage your documents and segregate RAM.

Set a cache as usable for session or client storage

Session

Once the cache is created, enabling session storage is easy. In your Application.cfc simply confirm sessionManagment is turned on and set the following two settings as well in the this scope:

component{
    this.name = "myApp";
		
    this.sessionManagement = true;
    this.sessionTimeout = createTimeSpan(0,0,30,0);
	
	// This is the name of the Couchbase cache to use
    this.sessionStorage = "SessionStorageCache";
	// Set this to true if you have more than 1 application
    this.sessionCluster = true;
}

Client

Storing your client scope in Couchbase is just as easy as session storagey. In your Application.cfc confirm clientManagement is turned on and set the following two settings in the this scope:

component{
    this.name = "myApp";
    
    this.clientManagement = true;
    this.clientTimeout = createTimeSpan(25,0,0,0);
	
	// This is the name of the Couchbase cache to use
    this.clientStorage = "CientStorageCache";
	// Set this to true if you have more than 1 application
    this.clientCluster = true;
    
}

Direct Java API

If you need more power or are familiar with the Couchbase Java API and want to use some features we haven't implemented yet, first let us know so we can consider that addition in future version, and then head over to the Java Client to get direct access to the Couchbase Java SDK.

Couchbase Java Client

The Couchbase extension registers a built-in function (BIF) called couchbaseGetClient() that you can call anywhere in your application to get a direct reference to the Couchbase Java Client that powers the Couchbase extension.

Remember, that there is a client instance for each Couchbase cache defined in the administrator. If you call the couchbaseGetClient() function with no parameters, you will get the client that corresponds with the default object cache (assuming it is Couchbase). Alternativley, you can pass in a cacheName parameter to specifiy the Couchbase cache connection.

Parameter Required Type Default Description
cacheName No string --- Definition of the cache used by name, when not set the "default Object Cache" defined in Lucee Administrator is used instead. An error will be thrown if the cache is not a Couchbase cache or does not exist.

// Get the Java client for the default object cache
myClient = couchbaseGetClient();

// Get the Java client for the queryStorage cache
myClient = couchbaseGetClient("queryStorage");  

// Set object directly into cache
myClient.set(key, expiry, value);

// get object directly into cache
myClient.get(key);

// Remove object direclty from cache
myClient.delete(key)

Don't forget to javaCast() and realize that you may have issues trying to use the keys that the Extension has set since you are bypassing our serialization schema as well.

The Couchbase Java client is fully documented here.

Ortus Couchbase Java Provider

There is one more BIF that the Couchbase extension will register with your Lucee installation. It is called couchbaseGetProvider() and returns an instance to the Java class that Ortus wrote that proxies between the Couchbase client and Lucee . It implements Lucee .commons.io.cache.Cache and you can read about its API in the Lucee .org/javadoc-4-0/ Lucee /commons/io/cache/Cache.html Lucee API docs.

For the most part you probably won't need this class, but there are a few methods in the Lucee cache interface that aren't available to you via CFML.

Parameter Required Type Default Description
cacheName No string --- Definition of the cache used by name, when not set the "default Object Cache" defined in Lucee Administrator is used instead. An error will be thrown if the cache is not a Couchbase cache or does not exist.

// Get the Ortus Java Provider for the default object cache
myProvider = couchbaseGetProvider();

// Get the Ortus Java Provider for the queryStorage cache
myProvider = couchbaseGetProvider( "queryStorage" );  

// Get all keys in the cache as a java.util.List 
keys = myProvider.keys();

// Get all the values in the cache as a java.util.List
values = myProvider.values();

One important thing to note is that values get and set by the Ortus provider will be correctly serialized for you unline direct access to the Couchbase Java client.

Debugging/Troubleshooting

Here's a general list of troubleshooting steps if you're getting errors. The log files are VERY important since the error Lucee throws doesn't always contain the information you need. You will have to crack open your Lucee logs to get the actual error out.

Help & Support

If you need any help related to our Couchbase Extension product, you can use our online help group at http://groups.google.com/a/ortussolutions.com/forum/#!forum/couchbase- Lucee -ext. If you need any type of custom consulting or support package hours, please contact us at consulting@ortussolutions.com or visit us at www.ortussolutions.com.