blob: edf8f928e4d5ec084e304ead2bedcfeb02d6ac7b [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
= Ignite Cassandra Integration Usage Examples
== Overview
As described in link:extensions-and-integrations/cassandra/configuration[configuration section], to configure Cassandra
as a cache store you need to set `CacheStoreFactory` for your Ignite caches to `org.apache.ignite.cache.store.cassandra.CassandraCacheStoreFactory`.
Below is an example of a typical configuration for Ignite cache to use Cassandra as a cache store. We will go step-by-step
through all the configuration items, further down. The example is taken from the unit tests resource file
`store/src/test/resources/org/apache/ignite/tests/persistence/blob/ignite-config.xml` of the Cassandra module source code.
[tabs]
--
tab:XML[]
[source, xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Cassandra connection settings -->
<import resource="classpath:org/apache/ignite/tests/cassandra/connection-settings.xml" />
<!-- Persistence settings for 'cache1' -->
<bean id="cache1_persistence_settings" class="org.apache.ignite.cache.store.cassandra.persistence.KeyValuePersistenceSettings">
<constructor-arg type="org.springframework.core.io.Resource" value="classpath:org/apache/ignite/tests/persistence/blob/persistence-settings-1.xml" />
</bean>
<!-- Persistence settings for 'cache2' -->
<bean id="cache2_persistence_settings" class="org.apache.ignite.cache.store.cassandra.persistence.KeyValuePersistenceSettings">
<constructor-arg type="org.springframework.core.io.Resource" value="classpath:org/apache/ignite/tests/persistence/blob/persistence-settings-3.xml" />
</bean>
<!-- Ignite configuration -->
<bean id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
<property name="cacheConfiguration">
<list>
<!-- Configuring persistence for "cache1" cache -->
<bean class="org.apache.ignite.configuration.CacheConfiguration">
<property name="name" value="cache1"/>
<property name="readThrough" value="true"/>
<property name="writeThrough" value="true"/>
<property name="cacheStoreFactory">
<bean class="org.apache.ignite.cache.store.cassandra.CassandraCacheStoreFactory">
<property name="dataSourceBean" value="cassandraAdminDataSource"/>
<property name="persistenceSettingsBean" value="cache1_persistence_settings"/>
</bean>
</property>
</bean>
<!-- Configuring persistence for "cache2" cache -->
<bean class="org.apache.ignite.configuration.CacheConfiguration">
<property name="name" value="cache2"/>
<property name="readThrough" value="true"/>
<property name="writeThrough" value="true"/>
<property name="cacheStoreFactory">
<bean class="org.apache.ignite.cache.store.cassandra.CassandraCacheStoreFactory">
<property name="dataSourceBean" value="cassandraAdminDataSource"/>
<property name="persistenceSettingsBean" value="cache2_persistence_settings"/>
</bean>
</property>
</bean>
</list>
</property>
<!-- Explicitly configure TCP discovery SPI to provide list of initial nodes. -->
<property name="discoverySpi">
<bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
<property name="ipFinder">
<!--
Ignite provides several options for automatic discovery that can be used
instead os static IP based discovery. For information on all options refer
to our documentation: http://apacheignite.readme.io/docs/cluster-config
-->
<!-- Uncomment static IP finder to enable static-based discovery of initial nodes. -->
<!--<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">-->
<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder">
<property name="addresses">
<list>
<!-- In distributed environment, replace with actual host IP address. -->
<value>127.0.0.1:47500..47509</value>
</list>
</property>
</bean>
</property>
</bean>
</property>
</bean>
</beans>
----
--
In the specified example we have two Ignite caches configured: `cache1` and `cache2`. So lets look at the configuration details.
Lets start from the cache configuration details. They are pretty similar for both caches (`cache1` and `cache2`) and looks like that:
[tabs]
--
tab:XML[]
[source, xml]
----
<bean class="org.apache.ignite.configuration.CacheConfiguration">
<property name="name" value="cache1"/>
<property name="readThrough" value="true"/>
<property name="writeThrough" value="true"/>
<property name="cacheStoreFactory">
<bean class="org.apache.ignite.cache.store.cassandra.CassandraCacheStoreFactory">
<property name="dataSourceBean" value="cassandraAdminDataSource"/>
<property name="persistenceSettingsBean" value="cache1_persistence_settings"/>
</bean>
</property>
</bean>
----
--
First of all we can see that `read-through` and `write-through` options are enabled:
[tabs]
--
tab:XML[]
[source, xml]
----
<property name="readThrough" value="true"/>
<property name="writeThrough" value="true"/>
----
--
which is required for Ignite cache, if you plan to use a persistent store for cache entries which expired.
You can optionally specify the `write-behind` setting if you prefer persistent store to be updated asynchronously:
[tabs]
--
tab:XML[]
[source, xml]
----
<property name="readThrough" value="true"/>
<property name="writeThrough" value="true"/>
----
--
The next important thing is `CacheStoreFactory` configuration:
[tabs]
--
tab:XML[]
[source, xml]
----
<property name="cacheStoreFactory">
<bean class="org.apache.ignite.cache.store.cassandra.CassandraCacheStoreFactory">
<property name="dataSourceBean" value="cassandraAdminDataSource"/>
<property name="persistenceSettingsBean" value="cache1_persistence_settings"/>
</bean>
</property>
----
--
You should use `org.apache.ignite.cache.store.cassandra.CassandraCacheStoreFactory` as a `CacheStoreFactory` for your
Ignite caches to utilize Cassandra as a persistent store. For `CassandraCacheStoreFactory` you should specify two required properties:
* `dataSourceBean` - name of the Spring bean, which specifies all the details about Cassandra database connection.
* `persistenceSettingsBean` - name of the Spring bean, which specifies all the details about how objects should be persisted into Cassandra database.
In the specified example `cassandraAdminDataSource` is a data source bean, which is imported into Ignite cache config file using this directive:
[tabs]
--
tab:XML[]
[source, xml]
----
<import resource="classpath:org/apache/ignite/tests/cassandra/connection-settings.xml" />
----
--
and `cache1_persistence_settings` is a persistence settings bean, which is defined in Ignite cache config file using such directive:
[tabs]
--
tab:XML[]
[source, xml]
----
<bean id="cache1_persistence_settings" class="org.apache.ignite.cache.store.cassandra.utils.persistence.KeyValuePersistenceSettings">
<constructor-arg type="org.springframework.core.io.Resource" value="classpath:org/apache/ignite/tests/persistence/blob/persistence-settings-1.xml" />
</bean>
----
--
Now lets look at the specification of `cassandraAdminDataSource` from `store/src/test/resources/org/apache/ignite/tests/cassandra/connection-settings.xml`
test resource.
Specifically,`CassandraAdminCredentials` and `CassandraRegularCredentials` are classes which extend
`org.apache.ignite.cache.store.cassandra.datasource.Credentials`. You are welcome to implement these classes and reference them afterwards.
[tabs]
--
tab:XML[]
[source, xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="cassandraAdminCredentials" class="org.my.project.CassandraAdminCredentials"/>
<bean id="cassandraRegularCredentials" class="org.my.project.CassandraRegularCredentials"/>
<bean id="loadBalancingPolicy" class="com.datastax.driver.core.policies.TokenAwarePolicy">
<constructor-arg type="com.datastax.driver.core.policies.LoadBalancingPolicy">
<bean class="com.datastax.driver.core.policies.RoundRobinPolicy"/>
</constructor-arg>
</bean>
<bean id="contactPoints" class="org.apache.ignite.tests.utils.CassandraHelper" factory-method="getContactPointsArray"/>
<bean id="cassandraAdminDataSource" class="org.apache.ignite.cache.store.cassandra.datasource.DataSource">
<property name="credentials" ref="cassandraAdminCredentials"/>
<property name="contactPoints" ref="contactPoints"/>
<property name="readConsistency" value="ONE"/>
<property name="writeConsistency" value="ONE"/>
<property name="loadBalancingPolicy" ref="loadBalancingPolicy"/>
</bean>
<bean id="cassandraRegularDataSource" class="org.apache.ignite.cache.store.cassandra.datasource.DataSource">
<property name="credentials" ref="cassandraRegularCredentials"/>
<property name="contactPoints" ref="contactPoints"/>
<property name="readConsistency" value="ONE"/>
<property name="writeConsistency" value="ONE"/>
<property name="loadBalancingPolicy" ref="loadBalancingPolicy"/>
</bean>
</beans>
----
--
For more details about Cassandra data source connection configuration visit the link:extensions-and-integrations/cassandra/configuration[integration configuration page].
Finally, the last piece which wasn't still described is persistence settings configuration. Lets look at the
`cache1_persistence_settings` from the `org/apache/ignite/tests/persistence/blob/persistence-settings-1.xml` test resource.
[tabs]
--
tab:XML[]
[source, xml]
----
<persistence keyspace="test1" table="blob_test1">
<keyPersistence class="java.lang.Integer" strategy="PRIMITIVE" />
<valuePersistence strategy="BLOB"/>
</persistence>
----
--
In the configuration above, we can see that Cassandra `test1.blob_test1` table will be used to store key/value objects for
**cache1** cache. Key objects of the cache will be stored as **integer** in `key` column. Value objects of the cache will be
stored as **blob** in `value` column. For more information about persistence settings configuration visit the
link:extensions-and-integrations/cassandra/configuration[integration configuration page].
Next sections will provide examples of persistence settings configuration for different kind of persistence strategies
(see more details about persistence strategies on the link:extensions-and-integrations/cassandra/configuration[integration configuration page].
== Example 1
Persistence setting for Ignite cache with keys of `Integer` type to be persisted as `int` in Cassandra and values of
`String` type to be persisted as `text` in Cassandra.
[tabs]
--
tab:XML[]
[source, xml]
----
<persistence keyspace="test1" table="my_table">
<keyPersistence class="java.lang.Integer" strategy="PRIMITIVE" column="my_key"/>
<valuePersistence class="java.lang.String" strategy="PRIMITIVE" />
</persistence>
----
--
Keys will be stored in `my_key` column. Values will be stored in `value` column (which is used by default if `column` attribute wasn't specified).
== Example 2
Persistence setting for Ignite cache with keys of `Integer` type to be persisted as `int` in Cassandra and values of `any`
type (you don't need to specify the type for **BLOB** persistence strategy) to be persisted as `blob` in Cassandra.
The only solution for this situation is to store value as a `BLOB` in Cassandra table.
[tabs]
--
tab:XML[]
[source, xml]
----
<persistence keyspace="test1" table="my_table">
<keyPersistence class="java.lang.Integer" strategy="PRIMITIVE" />
<valuePersistence strategy="BLOB"/>
</persistence>
----
--
Keys will be stored in `key` column (which is used by default if `column` attribute wasn't specified). Values will be stored in `value` column.
== Example 3
Persistence setting for Ignite cache with keys of `Integer` type and values of **any** type, both to be persisted as `BLOB` in Cassandra.
[tabs]
--
tab:XML[]
[source, xml]
----
<persistence keyspace="test1" table="my_table">
<!-- By default Java standard serialization is going to be used -->
<keyPersistence class="java.lang.Integer"
strategy="BLOB"/>
<!-- Kryo serialization specified to be used -->
<valuePersistence class="org.apache.ignite.tests.pojos.Person"
strategy="BLOB"
serializer="org.apache.ignite.cache.store.cassandra.serializer.KryoSerializer"/>
</persistence>
----
--
Keys will be stored in `key` column having `blob` type and using
https://docs.oracle.com/javase/tutorial/jndi/objects/serial.html[Java standard serialization, window=_blank]. Values will be stored in
`value` column having `blob` type and using https://github.com/EsotericSoftware/kryo[Kryo serialization, window=_blank].
== Example 4
Persistence setting for Ignite cache with keys of `Integer` type to be persisted as `int` in Cassandra and values of custom
POJO `org.apache.ignite.tests.pojos.Person` type to be dynamically analyzed and persisted into a set of table columns,
so that each POJO field will be mapped to appropriate table column. For more details about dynamic POJO fields discovery
refer to link:extensions-and-integrations/cassandra/configuration#persistencesettingsbean[PersistenceSettingsBean] documentation section.
[tabs]
--
tab:XML[]
[source, xml]
----
<persistence keyspace="test1" table="my_table">
<keyPersistence class="java.lang.Integer" strategy="PRIMITIVE"/>
<valuePersistence class="org.apache.ignite.tests.pojos.Person" strategy="POJO"/>
</persistence>
----
--
Keys will be stored in `key` column having `int` type.
Now lets imagine that the `org.apache.ignite.tests.pojos.Person` class has such an implementation:
[tabs]
--
tab:Java[]
[source, java]
----
public class Person {
private String firstName;
private String lastName;
private int age;
private boolean married;
private long height;
private float weight;
private Date birthDate;
private List<String> phones;
public void setFirstName(String name) {
firstName = name;
}
public String getFirstName() {
return firstName;
}
public void setLastName(String name) {
lastName = name;
}
public String getLastName() {
return lastName;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setMarried(boolean married) {
this.married = married;
}
public boolean getMarried() {
return married;
}
public void setHeight(long height) {
this.height = height;
}
public long getHeight() {
return height;
}
public void setWeight(float weight) {
this.weight = weight;
}
public float getWeight() {
return weight;
}
public void setBirthDate(Date date) {
birthDate = date;
}
public Date getBirthDate() {
return birthDate;
}
public void setPhones(List<String> phones) {
this.phones = phones;
}
public List<String> getPhones() {
return phones;
}
}
----
--
In this case Ignite cache values of the `org.apache.ignite.tests.pojos.Person` type will be persisted into a set of
Cassandra table columns using such dynamically configured mapping rule:
[opts="header"]
|===
| POJO field | Table column | Column type
| firstName | firstname | text
| lastName | lastname | text
| age | age | int
| married | married | boolean
| height | height | bigint
| weight | weight | float
| birthDate | birthdate | timestamp
|===
As you can see from the table above, `phones` field will not be persisted into table. That's because it's not of simple
java type which could be directly mapped to http://docs.datastax.com/en/developer/java-driver/1.0/java-driver/reference/javaClass2Cql3Datatypes_r.html[appropriate, window=_blank] Cassandra type.
Such kind of fields could be persisted into Cassandra only if you manually specify all mapping details for the object type
and if field type itself is implementing `java.io.Serializable` interface. In a such case field will be persisted into a
separate table column as `blob`. See more details in the next example.
== Example 5
Persistence setting for Ignite cache with keys of custom POJO `org.apache.ignite.tests.pojos.PersonId` and values of
custom POJO `org.apache.ignite.tests.pojos.Person` types, both to be persisted into a set of table columns based on
manually specified mapping rules.
[tabs]
--
tab:XML[]
[source, xml]
----
<persistence keyspace="test1" table="my_table" ttl="86400">
<!-- Cassandra keyspace options which should be used to create provided keyspace if it doesn't exist -->
<keyspaceOptions>
REPLICATION = {'class' : 'SimpleStrategy', 'replication_factor' : 3}
AND DURABLE_WRITES = true
</keyspaceOptions>
<!-- Cassandra table options which should be used to create provided table if it doesn't exist -->
<tableOptions>
comment = 'A most excellent and useful table'
AND read_repair_chance = 0.2
</tableOptions>
<!-- Persistent settings for Ignite cache keys -->
<keyPersistence class="org.apache.ignite.tests.pojos.PersonId" strategy="POJO">
<!-- Partition key fields if POJO strategy used -->
<partitionKey>
<!-- Mapping from POJO field to Cassandra table column -->
<field name="companyCode" column="company" />
<field name="departmentCode" column="department" />
</partitionKey>
<!-- Cluster key fields if POJO strategy used -->
<clusterKey>
<!-- Mapping from POJO field to Cassandra table column -->
<field name="personNumber" column="number" sort="desc"/>
</clusterKey>
</keyPersistence>
<!-- Persistent settings for Ignite cache values -->
<valuePersistence class="org.apache.ignite.tests.pojos.Person"
strategy="POJO"
serializer="org.apache.ignite.cache.store.cassandra.serializer.KryoSerializer">
<!-- Mapping from POJO field to Cassandra table column -->
<field name="firstName" column="first_name" />
<field name="lastName" column="last_name" />
<field name="age" />
<field name="married" index="true"/>
<field name="height" />
<field name="weight" />
<field name="birthDate" column="birth_date" />
<field name="phones" />
</valuePersistence>
</persistence>
----
--
These persistence settings looks rather complicated. Lets go step by step and analyse them.
Lets first look at the root tag:
[tabs]
--
tab:XML[]
[source, xml]
----
<persistence keyspace="test1" table="my_table" ttl="86400">
----
--
It specifies that Ignite cache keys and values should be stored in `test1.my_table` table and that data in each row
http://docs.datastax.com/en/cql/3.1/cql/cql_using/use_expire_c.html[expires, window=_blank] after `86400` sec which is `24` hours.
Then we can see the advanced settings for Cassandra keyspace. The setting will be used to create keyspace if it's not exist.
[tabs]
--
tab:XML[]
[source, xml]
----
<keyspaceOptions>
REPLICATION = {'class' : 'SimpleStrategy', 'replication_factor' : 3}
AND DURABLE_WRITES = true
</keyspaceOptions>
----
--
Then by analogy to keyspace setting we can see table advanced setting, which will be used only for table creation.
[tabs]
--
tab:XML[]
[source, xml]
----
<tableOptions>
comment = 'A most excellent and useful table'
AND read_repair_chance = 0.2
</tableOptions>
----
--
Next section specifies how Ignite cache keys should be persisted:
[tabs]
--
tab:XML[]
[source, xml]
----
<keyPersistence class="org.apache.ignite.tests.pojos.PersonId" strategy="POJO">
<!-- Partition key fields if POJO strategy used -->
<partitionKey>
<!-- Mapping from POJO field to Cassandra table column -->
<field name="companyCode" column="company" />
<field name="departmentCode" column="department" />
</partitionKey>
<!-- Cluster key fields if POJO strategy used -->
<clusterKey>
<!-- Mapping from POJO field to Cassandra table column -->
<field name="personNumber" column="number" sort="desc"/>
</clusterKey>
</keyPersistence>
----
--
Lets assume that `org.apache.ignite.tests.pojos.PersonId` has such implementation:
[tabs]
--
tab:Java[]
[source, java]
----
public class PersonId {
private String companyCode;
private String departmentCode;
private int personNumber;
public void setCompanyCode(String code) {
companyCode = code;
}
public String getCompanyCode() {
return companyCode;
}
public void setDepartmentCode(String code) {
departmentCode = code;
}
public String getDepartmentCode() {
return departmentCode;
}
public void setPersonNumber(int number) {
personNumber = number;
}
public int getPersonNumber() {
return personNumber;
}
}
----
--
In such case Ignite cache keys of `org.apache.ignite.tests.pojos.PersonId` type will be persisted into a set of Cassandra
table columns representing `PARTITION` and `CLUSTER` key using this mapping rule:
[opts="header"]
|===
| POJO field | Table column | Column type
| companyCode | company | text
| departmentCode | department | text
| personNumber | number | int
|===
In addition to that, combination of columns `(company, department)` will be used as Cassandra `PARTITION` key and column
`number` will be used as a `CLUSTER` key sorted in descending order.
Finally lets move to the last section, which specifies persistence settings for Ignite cache values:
[tabs]
--
tab:XML[]
[source, xml]
----
<valuePersistence class="org.apache.ignite.tests.pojos.Person"
strategy="POJO"
serializer="org.apache.ignite.cache.store.cassandra.serializer.KryoSerializer">
<!-- Mapping from POJO field to Cassandra table column -->
<field name="firstName" column="first_name" />
<field name="lastName" column="last_name" />
<field name="age" />
<field name="married" index="true"/>
<field name="height" />
<field name="weight" />
<field name="birthDate" column="birth_date" />
<field name="phones" />
</valuePersistence>
----
--
Lets assume `that org.apache.ignite.tests.pojos.Person` class has the same implementation like in link:extensions-and-integrations/cassandra/usage-examples#example-4[Example 4].
In this case Ignite cache values of `org.apache.ignite.tests.pojos.Person` type will be persisted into a set of Cassandra
table columns using such mapping rule:
[opts="header"]
|===
| POJO field | Table column | Column type
| firstName | first_name | text
| lastName | last_name | text
| age | age | int
| married | married | boolean
| height | height | bigint
| weight | weight | float
| birthDate | birth_date | timestamp
| phones | phones | blob
|===
Comparing to link:extensions-and-integrations/cassandra/usage-examples#example-4[Example 4] we can see that now `phones`
field will be serialized to `phones` column of `blob` type using https://github.com/EsotericSoftware/kryo[Kryo, window=_blank] serializer.
In addition to that, Cassandra secondary index will be created for the `married` column.