Search code examples
ignite

Apache Ignite 'Failed to prepare update plan' when executing SQL query on cache


I am playing around with different approaches on how to configure caches and tables in Ignite and then insert an entry via the SQL API using the .NET SDK.

I create two caches with each having a table. The first is created via CacheClientConfiguration and QueryEntities and the second using the 'CREATE TABLE...' DDL command. I then try to insert the same object (same values) into both tables using 'INSERT INTO...'. For the table created using the 'CREATE Table...' command it works, but the for the table created using QueryEntities i get an IgniteClientException stating: 'Failed to prepare update plan'. Both Insert commands look exactly the same (besides the table name).

What is the exception trying to tell me, why does the insert work for the second approach but not for the first?

See example code below.

Creating caches and tables:

public class ValueClass
{
  [QuerySqlField(IsIndexed = true)]
  public long Id { get; set; }

  [QuerySqlField]
  public string Content { get; set; }
}

var cache01 = igniteClient.CreateCache<long, ValueClass>(new CacheClientConfiguration
{
  Name = "Cache01",
  QueryEntities = new[]
  {
    new QueryEntity(
      typeof(long),
      typeof(ValueClass))
    {
      TableName = "table01"
    }
  },
  SqlSchema = "PUBLIC",
});

var cache02 = igniteClient.CreateCache<long, ValueClass>("Cache02");
cache02.Query(new SqlFieldsQuery(
  "CREATE TABLE IF NOT EXISTS table02 (" +
    "Id BIGINT PRIMARY KEY, " +
    "Content VARCHAR, " +
    ")" +
   "WITH " +
     "\"cache_name=Cache02, " +
     $"VALUE_TYPE={typeof(ValueClass)}" +
     $"\""
   )
   { Schema = "PUBLIC" });

Executing insert queries:

// EXCEPTION: Apache.Ignite.Core.Client.IgniteClientException: 'Failed to prepare update plan.'
cache01.Query(
  new SqlFieldsQuery(
    "INSERT INTO " +
      "table01 ( " +
        "Id, " +
        "Content ) " +
      "VALUES ( " +
        "?, " +
        "? )"
  )
  {
    Arguments = new object[]
    {
      3,
      "entry3"
    },
    Schema = "PUBLIC"
  });

// This one works
cache02.Query(
  new SqlFieldsQuery(
    "INSERT INTO " +
      "table02 ( " +
        "Id, " +
        "Content ) " +
      "VALUES ( " +
        "?, " +
        "? )"
  )
  {
    Arguments = new object[]
    {
      3,
      "entry3"
    },
    Schema = "PUBLIC"
  });

Exception:

Unhandled exception. Apache.Ignite.Core.Client.IgniteClientException: Failed to prepare update plan.
   at Apache.Ignite.Core.Impl.Client.Cache.CacheClient`2.HandleError[T](ClientStatusCode status, String msg)
   at Apache.Ignite.Core.Impl.Client.ClientSocket.DecodeResponse[T](BinaryHeapStream stream, Func`2 readFunc, Func`3 errorFunc)
   at Apache.Ignite.Core.Impl.Client.ClientSocket.DoOutInOp[T](ClientOp opId, Action`1 writeAction, Func`2 readFunc, Func`3 errorFunc)
   at Apache.Ignite.Core.Impl.Client.ClientFailoverSocket.DoOutInOp[T](ClientOp opId, Action`1 writeAction, Func`2 readFunc, Func`3 errorFunc)
   at Apache.Ignite.Core.Impl.Client.Cache.CacheClient`2.DoOutInOp[T](ClientOp opId, Action`1 writeAction, Func`2 readFunc)
   at Apache.Ignite.Core.Impl.Client.Cache.CacheClient`2.Query(SqlFieldsQuery sqlFieldsQuery)
   at ApacheIgniteConfigurationDemo.Worker.ExecuteAsync(CancellationToken stoppingToken) in C:\ApacheIgniteConfigurationDemo\Worker.cs:line 60
   at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
   at Program.<Main>$(String[] args) in C:\ApacheIgniteConfigurationDemo\Program.cs:line 26
   at Program.<Main>(String[] args) [StatusCode=Fail]

Ignite is running in a docker container using the default configuration.

Solution:

As pointed out by @Alexandr Shapkin (see Accepted Answer) I had to specify the "KeyFieldName" as I wanted to use the "Id" field of the POJO class as key.

Configuring the table like this worked for my case:

var cache01 = igniteClient.CreateCache<long, ValueClass>(new CacheClientConfiguration
{
  Name = "Cache01",
  QueryEntities = new[]
  {
      new QueryEntity(
        typeof(long),
        typeof(ValueClass))
      {
        TableName = "table01",
        KeyFieldName = nameof(ValueClass.Id)
      }
    },
  SqlSchema = "PUBLIC",
});

Another solution would have been to add the "_key" row to the Insert command:

cache01.Query(
  new SqlFieldsQuery(
    "INSERT INTO " +
      "table01 ( " +
        "_key, " +
        "Id, " +
        "Content ) " +
      "VALUES ( " +
        "?, " +
        "?, " +
        "? )"
  )
  {
    Arguments = new object[]
    {
        3 ,
        3,
        "entry3"
    },
    Schema = "PUBLIC"
  });

Solution

  • I believe this is because you didn't provide the _KEY to the first query explicitly and would like to keep it on your POJO model.

    Specify the key configuration explicitly using the following configuration and give it a try.

    var cache01 = igniteClient.CreateCache<long, ValueClass>(new CacheConfiguration()
                {
                    Name = "Cache01",
                    QueryEntities = new[]
                    {
                        new QueryEntity(
                            typeof(long),
                            typeof(ValueClass))
                        {
                            TableName = "table01",
                            KeyFieldName = "Id",
                            Fields = new List<QueryField>()
                            {
                                    new QueryField("Id", typeof(long)),
                                    new QueryField("Content", typeof(string)),
                            }
                        }
                    },
                    SqlSchema = "PUBLIC",
                });