Memcached

How to create a cache and a lock using Memcached.

Key takeaways

MemcachedCache Distributed cache that stores values in a Memcached store.
MemcachedLock Distributed lock that is implemented based on a Memcached store.

Introduction

In this tutorial, you will understand how to create and manage a cache and a distributed lock, both based on a Memcached store.

First, we will look at the pre-requisites. Then, we will see how to create and perform relevant operations through examples. We will finalize the tutorial with a practical example of how to use distributed locks.

Pre-requisites

In order to use these components, we need to install the Memcached module first. The following command shows how to do this:

npm install pip-services-memcached-nodex --save
dotnet add package PipServices3.Memcached
go get -u github.com/pip-services3-go/pip-services3-memcached-go@latest
Not available
pip install pip-services3-memcached
Not available

MemcachedCache

This component provides a way to create a distributed cache based on the Memcached database. In this section, we will learn how to install and perform CRUD operations with it.

Pre-requisites

To use this component, we need to import it first. This can be done with the following command:

import { MemcachedCache } from "pip-services-memcached-nodex";
using PipServices3.Memcached.Cache;
import (
    memcache "github.com/pip-services3-go/pip-services3-memcached-go/cache"
)
Not available
from pip_services3_memcached.cache import MemcachedCache
Not available

Component creation

Once we have imported our base class, we instantiate and configure it. Our main configuration parameters are the connection host and the port. In our case, we use our machine and the default port for Memcached. The following code explains how to do this:

import { ConfigParams } from "pip-services3-commons-nodex";

let cache = new MemcachedCache();

cache.configure(ConfigParams.fromTuples(
    "connection.host", "localhost",
    "connection.port", 11211
));

using PipServices3.Commons.Config;

var cache = new MemcachedCache();

cache.Configure(ConfigParams.FromTuples(
    "connection.host", "localhost",
    "connection.port", 11211
));

import (
	conf "github.com/pip-services3-go/pip-services3-commons-go/config"
)

cache := memcache.NewMemcachedCache()

cache.Configure(conf.NewConfigParamsFromTuples(
	"connection.host", "localhost",
	"connection.port", 11211,
))

Not available
cache = MemcachedCache()

from pip_services3_commons.config import ConfigParams

cache.configure(ConfigParams.from_tuples(
    "connection.host", "localhost",
    "connection.port", 11211
))
Not available

Once we have our component, we can connect it to our Memcached store through the open() method. This method requires a correlationId as input parameter only.

await cache.open("123");
await cache.OpenAsync("123");
cache.Open("123")
Not available
cache.open("123")
Not available

CRUD operations

The MemcachedCache class offers several methods to perform CRUD operations. They are:

Create and update

The store() method can be used to create a record or update its values if the record already exists. This method accepts the correlationId, the key and value, and the timeout as input parameters. It returns True if the operation was successful, and False otherwise. The following example explains its syntax:

await cache.store("123", "key1", "ABC", 5000); // Returns True if successful and False otherwise.
await cache.StoreAsync("123", "key1", "ABC", 5000); // Returns the stored value
res, err := cache.Store("123", "key1", "ABC", 5000)
Not available
cache.store("123", "key1", "ABC", 5000) # Returns True if successful and False otherwise.
Not available
Read

The retrieve() method can be used to obtain a stored record according to a given key. The following example describes its usage.

var value = await cache.RetrieveAsync<string>("123", "key1"); // Returns "ABC"
var value = await cache.RetrieveAsync<string>("123", "key1"); // Returns "ABC"
value, err := cache.Retrieve("123", "key1") // Returns "ABC"
Not available
value = cache.retrieve("123", "key1", )  # Returns "ABC"
Not available
Delete

We can use the remove() method to delete a record, which asks for a correlationId and a key as input parameters. An example of its syntax is:

await cache.remove("123", "key1"); // Returns True if successful and False otherwise
await cache.RemoveAsync("123", "key1");
cache.Remove("123", "key1") // Retruns: an error or nil for success
Not available
cache.remove("123",'key1') # Returns True if successful and False otherwise
Not available

MemcachedLock

This component allows us to create distributed locks based on Memcached caching and contains several functions for managing them. The next sections explain how to use it.

Pre-requisites

Before using it, we need to import this class. The following command shows how to do this:

import { MemcachedLock } from "pip-services-memcached-nodex";
using PipServices3.Memcached.Lock;
import (
    memlock "github.com/pip-services3-go/pip-services3-memcached-go/lock"
)
Not available
from pip_services3_memcached.lock import MemcachedLock
Not available

Component creation

To create a lock, we need to create an instance of this class and configure it with our database’s connection parameters. The following example connects to a Memcached store located in our machine and through the default port 11211.

import { ConfigParams } from "pip-services3-commons-nodex";

let lock = new MemcachedLock();

lock.configure(ConfigParams.fromTuples(
    "connection.host", "localhost",
    "connection.port", 11211
));

using PipServices3.Commons.Config;

var mLock = new MemcachedLock();


mLock.Configure(ConfigParams.FromTuples(
    "connection.host", "localhost",
    "connection.port", 11211
));

import (
	conf "github.com/pip-services3-go/pip-services3-commons-go/config"
)

lock := memlock.NewMemcachedLock()

lock.Configure(conf.NewConfigParamsFromTuples(
	"connection.host", "localhost",
	"connection.port", 11211,
))

Not available
lock = MemcachedLock()

from pip_services3_commons.config import ConfigParams

lock.configure(ConfigParams.from_tuples(
    "connection.host", "localhost",
    "connection.port", 11211
))
Not available

After defining our lock, we connect it to our Memcached store via the open() command.

await lock.open("123");
await mLock.OpenAsync("123");
lock.Open("123")
Not available
lock.open("123")
Not available

Managing the lock

Now, we can use our lock. For this, we have two primary operations: acquire and release, which can be managed with a set of methods available in this class.

Acquire

To acquire a lock, we have two methods. They are:

tryAcquireLock()

This method makes a single attempt at acquiring a lock. It asks for a correlationId, a key, and a timeout value in milliseconds as input parameters. It returns True if the operation was successful, and False otherwise.

await lock.tryAcquireLock("123", "key3566", 34000); // Returns True is successful and False otherwise
mLock.TryAcquireLock("123", "key3566", 34000); // Returns True is successful and False otherwise
lock.TryAcquireLock("123", "key3566", 34000) // Returns True is successful and False otherwise and err obj
Not available
lock.try_acquire_lock("123", "key3566", 34000) # Returns True is successful and False otherwise
Not available
acquireLock()

This method makes multiple attempts at acquiring a log. It accepts a correlationId, a key, a timeout, and a lock acquisition timeout in milliseconds as input parameters. The following example shows how to use it.

await lock.acquireLock("123", "key1", 3000, 1000);
mLock.AcquireLock("123", "key1", 3000, 1000);
lock.AcquireLock("123", "key1", 3000, 1000)
Not available
lock.acquire_lock("123", "key1", 3000, 1000)
Not available
Release

Once used, we can release the lock with the releaseLock() method, which takes a correlationId and a key as input parameters. Its usage is as follows:

await lock.releaseLock("123", "key1");
mLock.ReleaseLock("123", "key1");
lock.ReleaseLock("123", "key1")
Not available
lock.release_lock("123", "key1")
Not available

Example

Now that we understand how to create and manage a MemcachedLock, we can summarize all the operations in one example. In it, we create an instance of the component, configure it and connect to Memcached. We use a local store and the default port.

Then, we acquire the lock and perform some operations. Once the processing is done, we release the lock. Our code looks something like this:

import { MemcachedLock } from "pip-services-memcached-nodex";
import { ConfigParams } from "pip-services3-commons-nodex";

export async function main() {

    var lock = new MemcachedLock();

    lock.configure(ConfigParams.fromTuples(
        "connection.host", "localhost",
        "connection.port", 11211
    ));

    await lock.open("123");
    await lock.acquireLock("123", "key1", 3000, 1000);

    try {
        // Processing...
    }
    finally {
        await lock.releaseLock("123", "key1");
    }

    await lock.close("123");
}

using PipServices3.Memcached.Lock;
using PipServices3.Commons.Config;

var mLock = new MemcachedLock();

mLock.Configure(ConfigParams.FromTuples(
    "connection.host", "localhost",
    "connection.port", 11211
));

mLock.OpenAsync("123").Wait();
mLock.AcquireLock("123", "key1", 3000, 1000);

try
{
    // Processing...
}
finally
{
    mLock.ReleaseLock("123", "key1");
}

mLock.CloseAsync("123").Wait();

import (
	conf "github.com/pip-services3-go/pip-services3-commons-go/config"
	memlock "github.com/pip-services3-go/pip-services3-memcached-go/lock"
)

func main() {
	lock := memlock.NewMemcachedLock()
	lock.Configure(conf.NewConfigParamsFromTuples(
		"connection.host", "localhost",
		"connection.port", 11211,
	))

	lock.Open("123")
	defer lock.Close("123")

	lock.AcquireLock("123", "key1", 3000, 1000)
	defer lock.ReleaseLock("123", "key1")

	// Processing...
}

Not available
from pip_services3_memcached.lock import MemcachedLock
from pip_services3_commons.config import ConfigParams

lock.configure(ConfigParams.from_tuples(
    "connection.host", "localhost",
    "connection.port", 11211
))
        
lock.open("123")
lock.acquire_lock("123", "key1", 3000, 1000)
try:
    # Processing...
    pass
finally:
    lock.release_lock("123", "key1")

lock.close("123")
Not available

Wrapping up

This tutorial taught us how to create distributed caches and locks using a Memcached store. First, we learned how to define our cache and perform CRUD operations on records stored in our cache. Then, we created a distributed lock and saw how to manage it. We finished the explanations with an example that showed how to practically use a MemcachedLock with a processing task.