I have a method that is inserting into a table an Id, a set of UDT, and a date.
public class MessageQueueNext
{
public string Id { get; set; }
public List<UDTMessage> Messages { get; set; }
public DateTimeOffset Updated { get; set; }
}
private static Lazy<PreparedStatement> InsertStatement = new
Lazy<PreparedStatement>(
() => {
return CassandraDB.Instance.Session.Prepare
(
@"INSERT INTO SomeTable
(id, messages, updated)
VALUES (?, ?, ?)"
);
});
public async Task InsertAsync(SomeObj obj)
{
var statement = BindValues(
InsertStatement.Value,
new dynamic[] {
obj.Id, obj.Messages, obj.Updated
}
);
await CassandraDB.Instance.Session.ExecuteAsync(statement);
}
private BoundStatement BindValues(PreparedStatement ps, dynamic[] values)
{
var statement = ps.Bind
(
values[0] ?? Unset.Value, values[1] ?? new List<UDTMessage>(), values[2] ?? Unset.Value
);
return statement;
}
At some point this method throws an exception.
Collection was modified; enumeration operation may not execute. at System.Collections.Generic.List`1.Enumerator.MoveNextRare() at Cassandra.Serialization.CollectionSerializer.Serialize(UInt16 protocolVersion, IEnumerable value) at Cassandra.Serialization.GenericSerializer.Serialize(ProtocolVersion version, Object value) at Cassandra.QueryProtocolOptions.Write(FrameWriter wb, Boolean isPrepared) at Cassandra.Requests.BaseRequest.WriteFrame(Int16 streamId, MemoryStream stream, ISerializer connectionSerializer) at Cassandra.OperationState.WriteFrame(Int16 streamId, MemoryStream memoryStream, ISerializer serializer, Int64 timestamp) at Cassandra.Connections.Connection.RunWriteQueueAction() --- End of stack trace from previous location where exception was thrown ---
Can anyone help me with what could possibly cause that? As I'm understanding the obj.Messages is changed somewhere down the road? Or like it's possible that it's changed in a function that calls InsertAsync(...), but right after calling InsertAsync(...) it changes the obj.Messages value?
Method that is calling InsertAsync(...)
public async Task PersistMessageQueue(UDTMessage msg)
{
var DAO = new DAO();
var list = await _cache.GetMessages(msg.Id);
if (list == null)
{
list = new List<UDTMessage>();
}
list.Add(msg);
var someObj = new SomeObj();
someObj.ToId = msg.Id;
someObj.Messages = list;
someObj.Updated = DateTimeOffset.UtcNow;
await DAO.InsertAsync(messageQueueNext);
_cache.AddElement(msg);
}
Looking at the parent method (from your comment):
public async Task PersistMessageQueue(UDTMessage msg) {
var DAO = new DAO();
var list = await _cache.GetMessages(msg.Id);
if (list == null)
{
list = new List<UDTMessage>();
}
list.Add(msg);
var someObj = new SomeObj();
someObj.ToId = msg.Id;
someObj.Messages = list;
someObj.Updated = DateTimeOffset.UtcNow;
await DAO.InsertAsync(messageQueueNext);
_cache.AddElement(msg);
}
It looks like you are storing the List object in a cache, passing it to the driver but each call to PersistMessageQueue() will modify this List object so you might run into the issue where the driver is iterating through the List object while another call to PersistMessageQueue() is modifying that same List object.
The solution is to clone the List object before calling Session.ExecuteAsync() if this is what is causing the issue.