Friday, 20 October 2017

Cache Store Batch Operations

Infinispan 9.1.x introduces batch write and delete operations for cache stores. The introduction of batching should greatly improve performance when utilising write-behind cache stores, using putAll operations and committing transactions in non-transactional stores.

==

==

==

CacheWriter Interface Additions

The CacheWriter interface has been extended so that it exposes two additional methods: deleteBatch and writeBatch.  For the sake of backwards compatibility a default implementation of these methods is provided, however if your cache store is able to utilise batching we strongly recommend you create your  own implementations. The additional methods and docs are show below: 

Updated Stores

Currently the JDBC, JPA, RocksDB and Remote stores have all been modified to take advantage of these latest changes.

==

==

Configuration Changes

As each store implementations has different batching capabilities, it was necessary to introduce a max-batch-size attribute to the AbstractStoreConfiguration. This attribute defines the maximum number of entries that should be included in a single batch operation to the store. If a value less than one is provided, then the underlying store implementation should not place a upper limit on the number of entries in a batch. 

Deprecated Attributes

Both TableManipulationConfiguration#batchSize and JpaStoreConfiguration#batchSize have been deprecated, as they serve the same purpose as AbstractStoreConfiguration#maxBatchSize.

Store Benchmark

To measure the impact of batch writes on Cache.putAll, we created a simple benchmark to compare the performance of Infinispan 9.1.1.Final (with batching) and 9.0.3.Final (without).  The benchmark consisted of 20 threads inserting 100000 cache entries as fast as possible into a cache via putAll; with each putAll operation containing 20 cache entries and the max-batch-size of each store being set to 20. The table below shows the average time taken for each store type after the benchmark was executed three times.

Store Type 9.0.3.Final 9.1.1-Final Latency Decrease

JdbcStringBasedStore

29368ms

2597ms

91.12%

JPAStore

30798ms

16640ms

45.97%

RocksDBStore

1164ms

209ms

82.04%

The benchmark results above clearly show that performance is increased dramatically when utilising batch updates at the store level.

Conclusions

Infinispan 9.1.x introduces batching capabilities to the CacheWriter interface in order to improve performance. If you currently utilise a custom cache store, we strongly recommend that you provide your own implementation of the delete and write batch methods. 

If you have any feedback on the CacheWriter changes, or would like to request some new features/optimisations, let us know via the forumissue tracker or the #infinispan channel onhttp://webchat.freenode.net/?channels=%23infinispan[ Freenode].

Posted by Ryan Emerson on 2017-10-20
Tags: jdbc rocksdb jpa leveldb cache store

Friday, 10 March 2017

JDBC Migrator or: How I Learned to Stop Worrying About Buckets and Utilise the JdbcStringBasedStore!

Infinispan 9 has introduced many improvements to its marshalling codebase in order to improve performance and allow for greater flexibility. Consequently, data marshalled and persisted by Infinispan 8.x is no longer compatible with Infinispan 9.x. Furthermore, as part of our ongoing efforts to improve the cache stores provided by Infinispan, we have removed both the JdbcBinaryStore and JdbcMixedStore in Infinispan 9.0.

To assist users migrating from Infinispan 8.x, we have created the JDBC Migrator that enables existing JDBC stores to be migrated to Infinispan 9’s JdbcStringBasedStore.

No More Binary Keyed Stores!

The original intention of the JdbcBinaryStore was to provide greater flexibility over the JdbcStringBasedStore as it did not require a Key2StringMapper implementation.  This was achieved by utilising the hashcode of an entries key for a table’s ID column entry.  However, due to the possibility of hash collisions all entries had to be placed inside a Bucket object which was then serialised and inserted into the underlying table. Utilising buckets in this manner was far from optimal as each read/write to the underlying table required an existing bucket for a given hash to be retrieved, deserialised, updated, serialised and then re-inserted back into the db.

Introducing JDBC Migrator

The JDBCMigrator is a standalone application that takes a single argument, the path to a .properties file which must contain the configuration properties for both the source and target stores.  To use the migrator you need the infinispan-tools-9.x.jar, as well as the jdbc drivers required by your source and target databases, on your classpath.

An example maven pom that launches the migrator via mvn exec:java is presented below:

Migration Examples

Below are several example .properties files used for migrating various stores, however an exhaustive list of all available properties can be found in the Infinispan user guide.  

* *

Before attempting to migrate your existing stores please ensure you have backed up your database! * *

8.x JdbcBinaryStore → 9.x JdbcStringBasedStore

The most important property to set in this example is "source.marshaller.type=LEGACY" as this instructs the migrator to utilise the Infinispan 8.x marshaller to unmarshall data stored in your existing DB tables. 

If you specified custom AdvancedExternalizer implementations in your Infinispan 8.x configuration, then it is necessary for you to specify these in the migrator configuration and ensure that they are available on the migrators classpath.  To Specify the AdvancedExternalizers to load, it is necessary to define the "source.marshaller.externalizers" property with a comma-separated list of class names. If an ID was explicitly set for your externalizer, then it is possible to prepend the externalizers class name with "<id>:" to ensure the IDs is respected by the marshaller. 

TwoWayKey2StringMapper Migration

As well as using the JDBC Migrator to migrate from Infinispan 8.x, it is also possible to utilise it to migrate from one DB dialect to another or to migrate from one TwoWayKey2StringMapper implementation to another. 

Summary

Infinispan 9 stores are no longer compatible with Infinispan 8.x stores due to internal marshalling changes. Furthermore, the JdbcBinary and JdbcMixed stores have been removed due to their poor performance characteristics.  To aid users in their transition from Infinispan 8.x we have created the JDBC Migrator to enable users to migrate their existing JDBC stores.

If you’re a user of the JDBC stores and have any feedback on the latest changes, let us know via the forum, issue tracker or the #infinispan channel on Freenode. 

Posted by Ryan Emerson on 2017-03-10
Tags: jdbc cache stores migration

Monday, 05 December 2016

Composing the Infinispan Docker image

In the previous post we showed how to manipulate the Infinispan Docker container configuration at both runtime and boot time.

Before diving into multi-host Docker usage, in this post we’ll explore how to create multi-container Docker applications involving Infinispan with the help of Docker Compose.

For this we’ll look at a typical scenario of an Infinispan server backed by an Oracle database as a cache store.

All the code for this sample can be found on github.

 

Infinispan with Oracle JDBC cache store

 

In order to have a cache with persistence with Oracle, we need to do some configuration: configure the driver in the server, create the data source associated with the driver, and configure the cache itself with JDBC persistence.

Let’s take a look at each of those steps:

Obtaining and configuring the driver

The driver (ojdbc6.jar) should be downloaded and placed in the 'driver' folder of the sample project.

The module.xml declaration used to make it available on the server is as follows:

Configuring the Data source

The data source is configured in the "datasource" element of the server configuration file as shown below:

and inside the "datasource/drivers" element, we need to declare the driver:

Creating the cache

The last piece is to define a cache with the proper JDBC Store:

Putting all together

From now on, without using Docker we’d be ready to download and install Oracle following the specific instructions for your OS, then download the Infinispan Server, edit the configuration files, copy over the driver jar, figure out how to launch the database and server, taking care not to have any port conflicts.

If it sounds too much work, it’s because it really is. Wouldn’t it be nice to have all these wired together and launched with a simple command line? Let’s take a look at the Docker way next. 

Enter Docker Compose

Docker Compose is a tool part of the Docker stack to facilitate configuration, execution and management of related Docker containers.

By describing the application aspects in a single yaml file, it allows centralized control of the containers, including custom configuration and parameters, and it also allows runtime interactions with each of the exposed services.

Composing Infinispan

Our Docker Compose file to assemble the application is given below:

It contains two services:

  • one called oracle that uses the wnameless/oracle-xe-11g Docker image, with an environment variable to allow remote connections.

  •  another one called *infinispan* that uses version 8.2.5.Final of the Infinispan Server image. It is launched with a custom command pointing to the changed configuration file and it also mounts two volumes in the container: one for the driver and its module.xml and another for the folder holding our server xml configuration.

Launching

To start the application, just execute

To inspect the status of the containers:

To follow the Infinispan server logs, use:

Infinispan usually starts faster than the database, and since the server waits until the database is ready (more on that later), keep an eye in the log output for "Infinispan Server 8.2.5.Final (WildFly Core 2.0.10.Final) started". After that, both Infinispan and Oracle are properly initialized.

Testing it

Let’s insert a value using the Rest endpoint from Infinispan and verify it was saved to the Oracle database:

To check the Oracle database, we can attach to the container and use Sqlplus:

Other operations

It’s also possible to increase and decrease the number of containers for each of the services:

A thing or two about startup order

 

When dealing with dependent containers in Docker based environments, it’s highly recommended to make the connection obtention between parties robust enough so that the fact that one dependency is not totally initialized doesn’t cause the whole application to fail when starting.

Although Compose does have a depends_on instruction, it simply starts the containers in the declared order but it has no means to detected when a certain container is fully initialized and ready to serve requests before launching a dependent one.

One may be tempted to simply write some glue script to detect if a certain port is open, but that does not work in practice: the network socket may be opened, but the background service could still be in transient initialization state.

The recommended solution for this it to make whoever depends on a service to retry periodically until the dependency is ready. On the Infinispan + Oracle case, we specifically configured the data source with retries to avoid failing at once if the database is not ready:

When starting the application via Compose you’ll notice that Infinispan print some WARN with connection exceptions until Oracle is available: don’t panic, this is expected!

Conclusion

Docker Compose is a powerful and easy to use tool to launch applications involving multiple containers: in this post it allowed to start Infinispan plus Oracle with custom configurations with a single command. It’s also a handy tool to have during development and testing phase of a project, specially when using/evaluating Infinispan with its many possible integrations.

Be sure to check other examples of using Docker Compose involving Infinispan: the Infinispan+Spark Twitter demo, and the Infinispan+Apache Flink demo.

Posted by Gustavo on 2016-12-05
Tags: compose jdbc docker persistence server modules oracle cache store

News

Tags

JUGs alpha as7 asymmetric clusters asynchronous beta c++ cdi chat clustering community conference configuration console data grids data-as-a-service database devoxx distributed executors docker event functional grouping and aggregation hotrod infinispan java 8 jboss cache jcache jclouds jcp jdg jpa judcon kubernetes listeners meetup minor release off-heap openshift performance presentations product protostream radargun radegast recruit release release 8.2 9.0 final release candidate remote query replication queue rest query security spring streams transactions vert.x workshop 8.1.0 API DSL Hibernate-Search Ickle Infinispan Query JP-QL JSON JUGs JavaOne LGPL License NoSQL Open Source Protobuf SCM administration affinity algorithms alpha amazon anchored keys annotations announcement archetype archetypes as5 as7 asl2 asynchronous atomic maps atomic objects availability aws beer benchmark benchmarks berkeleydb beta beta release blogger book breizh camp buddy replication bugfix c# c++ c3p0 cache benchmark framework cache store cache stores cachestore cassandra cdi cep certification cli cloud storage clustered cache configuration clustered counters clustered locks codemotion codename colocation command line interface community comparison compose concurrency conference conferences configuration console counter cpp-client cpu creative cross site replication csharp custom commands daas data container data entry data grids data structures data-as-a-service deadlock detection demo deployment dev-preview development devnation devoxx distributed executors distributed queries distribution docker documentation domain mode dotnet-client dzone refcard ec2 ehcache embedded embedded query equivalence event eviction example externalizers failover faq final fine grained flags flink full-text functional future garbage collection geecon getAll gigaspaces git github gke google graalvm greach conf gsoc hackergarten hadoop hbase health hibernate hibernate ogm hibernate search hot rod hotrod hql http/2 ide index indexing india infinispan infinispan 8 infoq internationalization interoperability interview introduction iteration javascript jboss as 5 jboss asylum jboss cache jbossworld jbug jcache jclouds jcp jdbc jdg jgroups jopr jpa js-client jsr 107 jsr 347 jta judcon kafka kubernetes lambda language learning leveldb license listeners loader local mode lock striping locking logging lucene mac management map reduce marshalling maven memcached memory migration minikube minishift minor release modules mongodb monitoring multi-tenancy nashorn native near caching netty node.js nodejs non-blocking nosqlunit off-heap openshift operator oracle osgi overhead paas paid support partition handling partitioning performance persistence podcast presentation presentations protostream public speaking push api putAll python quarkus query quick start radargun radegast react reactive red hat redis rehashing releaase release release candidate remote remote events remote query replication rest rest query roadmap rocksdb ruby s3 scattered cache scripting second level cache provider security segmented server shell site snowcamp spark split brain spring spring boot spring-session stable standards state transfer statistics storage store store by reference store by value streams substratevm synchronization syntax highlighting tdc testing tomcat transactions tutorial uneven load user groups user guide vagrant versioning vert.x video videos virtual nodes vote voxxed voxxed days milano wallpaper websocket websockets wildfly workshop xsd xsite yarn zulip

back to top