Search code examples
ignite

Apache Ignite "Update of composite key column is not supported"


I have a cache with an AffinityKey as Key and want to insert an entry. If i try to insert the entry using a SQL-Insert statement i get an exception stating "Update of composite key column is not supported" which i guess is because the AffinityKey is a composite key. On https://ignite.apache.org/docs/latest/SQL/indexes#indexing-nested-objects i have read that if you are using indexed nested objects you will no longer be able to use insert or update statements but it is not mentioned on https://ignite.apache.org/docs/latest/data-modeling/affinity-collocation for composite keys.

Does using composite keys also cause insert and update statements to no longer be usable or am i missing something?

Example:

// creating cache using QueryEntities
cache = ignite.CreateCache<AffinityKey, MyClass>(new CacheClientConfiguration(cacheConfiguration)
  {
    Name = "MyCache",
    QueryEntities = new[]
    {
      new QueryEntity(typeof(AffinityKey), typeof(MyClass)){ TableName = "MyTable" }
    },
  });

// insert statement
cache.Query(
  new SqlFieldsQuery(
    "INSERT INTO " +
      "MyTable ( " +
        "_key, " +
        $"{nameof(MyClass.Text)} ) " +
      "VALUES ( " +
        "?, " +
        "? )"
  )
  {
    Arguments = new object[]
    {
      new AffinityKey("Key1", "AffinityKey1"),
      "TextValue"
    },
  });

Solution: As described below by @Alexandr Shapkin, setting "IGNITE_SQL_ALLOW_KEY_VAL_UPDATES" to true makes it possible to update entries via SQL that have been added using a composite key.

For anyone interested https://ignite.apache.org/docs/latest/setup#setting-ignite-system-properties shows how to list available ignite properties/environment variables with a brief description.


Solution

  • You are mixing the concepts.

    In Apache Ignite you can access or insert your data multiple ways. One would be to use Cache API, like cache#get, cache#put, another option would be - using SQL, like INSERT, SELECT, etc.

    Since eventually everything is just a key-value pair undeneath, Ignite provide a special SQL _key and _val properties to access the corresponded values.

    The thing is - if you don't need to use Cache API and SQL interchangeably and are OK using only, say, SQL, you don't set _key at all.

    On the other hand, if you need to use both APIs, say, defined with QueryEntity just like in your example, you need to specify _key field properly.

    Consider this example:

    IgniteCache<AffinityKey<Long>, Person> colPersonCache = Ignition.ignite().cache(PERSON_CACHE);
    
    Person p1 = new Person(org1, "John", "Doe", 2000, "John Doe has Master Degree.");
    
    colPersonCache.put(p1.key(), p1);
    

    ...

    it's fine to do this:

        colPersonCache.query(new SqlFieldsQuery(
            "INSERT INTO Person (id, orgId, firstName, lastName, salary, resume) " +
                "VALUES ( ?, ?, ?, ?, ?, ?)")
            .setArgs(6, 2, "first", "last", 1, "Master Degree")).getAll();
    

    But in that case, you won't be able to locate the record using cache API:

    // the following line returns null, but you can use SQL to locate the record
    colPersonCache.get(new AffinityKey<Long>(6L,2L)); 
    

    But if you use _key:

    colPersonCache.query(new SqlFieldsQuery(
                "INSERT INTO Person (_key, id, orgId, firstName, lastName, salary, resume) " +
                    "VALUES (?, ?, ?, ?, ?, ?, ?)")
                .setArgs(new AffinityKey<Long>(6L,2L), 6, 2, "first", "last", 1, "Master Degree")).getAll();
    

    Cache API now does return the value:

    colPersonCache.get(new AffinityKey<Long>(6L,2L)); 
    
    //Person [id=6, orgId=2, lastName=last, firstName=first, salary=1.0, resume=Master Degree]
    

    Note.

    There is a special restriction to avoid possible key desync, and in order to make the latest query (and the original in the question) work, you have to set the following enviroment property:

    System.setProperty(IgniteSystemProperties.IGNITE_SQL_ALLOW_KEY_VAL_UPDATES, "true");