Cassandra Csharp驱动程序中的准备语句缓存问题

本文关键字:语句 缓存 问题 Csharp 驱动程序 Cassandra | 更新日期: 2023-09-27 18:28:59

我相信我在Cassandra csharp驱动程序(版本2.7.3)的StatementFactory中发现了一个准备好的语句缓存逻辑错误。以下是用例。

Guid key = Guid.NewGuid(); // your key
ISession session_foo = new Session("foo"); //This is pseudo code
ISession session_bar = new Session("bar");
var foo_mapper = new Mapper(session_foo); //table foo_bar
var bar_mapper = new Mapper(session_bar); //table foo_bar
await Task.WhenAll(
   foo_mapper.DeleteAsync<Foo>("WHERE id = ?", key),
   bar_mapper.DeleteAsync<Bar>("WHERE id = ?", key));

我们发现,在运行此删除之后,只有第一个请求成功。在StatementFactory 的源代码中潜水后

public Task<Statement> GetStatementAsync(ISession session, Cql cql)
    {
        if (cql.QueryOptions.NoPrepare)
        {
            // Use a SimpleStatement if we're not supposed to prepare
            Statement statement = new SimpleStatement(cql.Statement, cql.Arguments);
            SetStatementProperties(statement, cql);
            return TaskHelper.ToTask(statement);
        }
        return _statementCache
            .GetOrAdd(cql.Statement, session.PrepareAsync)
            .Continue(t =>
            {
                if (_statementCache.Count > MaxPreparedStatementsThreshold)
                {
                    Logger.Warning(String.Format("The prepared statement cache contains {0} queries. Use parameter markers for queries. You can configure this warning threshold using MappingConfiguration.SetMaxStatementPreparedThreshold() method.", _statementCache.Count));
                }
                Statement boundStatement = t.Result.Bind(cql.Arguments);
                SetStatementProperties(boundStatement, cql);
                return boundStatement;
            });
    }

您可以看到缓存仅使用cql语句。在我们的例子中,我们在不同的键空间(也称为会话)中有相同的表名。我们在两个查询中的cql语句看起来相同。ie从foo_bar中删除id=

如果非要我猜测的话,我会说一个简单的修复方法是将cql语句和keyspace组合在一起作为缓存键。

以前有其他人遇到过这个问题吗?

Cassandra Csharp驱动程序中的准备语句缓存问题

作为一个简单的解决方法,我使用DoNotPrepare 跳过缓存

await _mapper.DeleteAsync<Foo>(Cql.New("WHERE id = ?", key).WithOptions(opt => opt.DoNotPrepare()));

我还发现Datastax 存在一个悬而未决的问题

有一个打开的票证可以修复这种行为。

作为一种变通方法,您可以在创建Mapper:时使用不同的MappingConfiguration实例

ISession session1 = cluster.Connect("ks1");
ISession session2 = cluster.Connect("ks2");
IMapper mapper1 = new Mapper(session1, new MappingConfiguration());
IMapper mapper2 = new Mapper(session2, new MappingConfiguration());

或者,您可以重用单个ISession实例,并完全限定查询以包含密钥空间。

MappingConfiguration.Global.Define(
   new Map<Foo>()
      .TableName("foo")
      .KeyspaceName("ks1"),
   new Map<Bar>()
      .TableName("bar")
      .KeyspaceName("ks2"));
ISession session = cluster.Connect();
IMapper mapper = new Mapper(session);