question

dtln812 avatar image
dtln812 asked Erick Ramirez edited

Cassandra collections serializer throws exception

I have a method that is inserting into a table an Id, a set of UDT, and a date.

        public async Task InsertAsync(SomeObject obj)
        {
            var statement = BindValues(
                InsertStatement.Value,
                new dynamic[] {
                    obj.Id, obj.Messages, obj.Updated
                }
            );


            await CassandraDB.Instance.Session.ExecuteAsync(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?

csharp driver
10 |1000

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.

joao.reis avatar image
joao.reis answered

I replied to your stackoverflow question but I'll post the answer here as well.

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.

Share
10 |1000

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.

dtln812 avatar image
dtln812 answered

So it was a problem that obj.Messages was taken from the Cache layer and passed further to the Insert method. But multiple tasks were using the Cache layer (modifying the list fetched), so the foreach inside the Collection Serializer throws exception. So the solution was to pass a copy of the list from Cache, rather than the list from cache directly.

Share
10 |1000

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.