Error occured when fetch using SyncManager ChannelCollection (

I am use Sendbird syncmanager v1.1.22 and i get ConcurrentModificationException when calling fetch function on ChannelCollection, i don’t really know how could it happen, this is the log list that show when its happen:

Caused by java.util.ConcurrentModificationException
       at java.util.ArrayList$Itr.next(ArrayList.java:860)
       at java.util.AbstractCollection.toString(AbstractCollection.java:461)
       at java.lang.String.valueOf(String.java:2924)
       at java.lang.StringBuilder.append(StringBuilder.java:132)
       at com.sendbird.syncmanager.DatabaseController.getGroupChannelListQueryFromDb(DatabaseController.java:1202)
       at com.sendbird.syncmanager.ChannelContainer.getGroupChannelListQuery(ChannelContainer.java:200)
       at com.sendbird.syncmanager.ChannelCollection.fetch(ChannelCollection.java:208)
       at com.sendbird.syncmanager.ChannelCollection.fetch(ChannelCollection.java:117)

any suggestion how to resolve this problem?
Thanks

Hi @zulfikar18, thank you for reporting this problem to us.

It seems that the problem originated from GroupChannelListQuery#mCustomTypes being updated while the sync manager is reading data from it. We will fix this as soon as possible.

Before fixing it on our end, if you are writing to GroupChannelListQuery#mCustomTypes, trying not to write to GroupChannelListQuery#mCustomTypes might help. This value can be acquired by GroupChannelListQuery#getCustomTypesFilter and GroupChannelChangeLogsParams#getCustomTypes. If you are writing to a list acquired by one of these methods, try not to write to them while you are waiting for the fetch. It would be great if this solves your problem, but the problem could have originated from our end so there is no guarantee.

Thank you again for the bug report and please feel free to post follow-up questions or additional inquiries.

The exception sometimes happened even if we don’t write the custom type again. Basically, this is the code:

    val query = GroupChannel.createMyGroupChannelListQuery()
    query.isIncludeEmpty = isIncludeEmptyGroup
    query.limit = queryLimit
    query.customTypesFilter = customTypes
    val channelCollection = ChannelCollection(query)
    ...
    channelCollection.fetch(handler)

Hi @wisnup , thank you for the additional info. It helps us track down the causes. It seems that the bug comes from within the sync manager. We will fix it and update it here once the bugfix is released. While waiting for the release, if you need a workaround, please consider catching the exception and trying fetch again. I’m sorry that this is not the most elegant solution. Thank you.

Catching exception is not possible because the SendBird fetch process run on different thread.

    private void fetch(final int limit, final CompletionHandler handler) {
        Logger.d(this.getName() + " fetch() limit = " + limit);
        this.runOnSingleThreadPool(new Runnable() {
             public void run() {

Unless you mean UncaughtException. We consider that too but yeah we’re still looking for better solution.

@Cjeon according to this issue, do you have any estimated time to release new version for fixing this issue, so we can decide what solution that we take, if it take longer time we will use your suggestion to catch the exception by use this method, https://stackoverflow.com/questions/6546193/how-to-catch-an-exception-from-a-thread

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("Caught " + e);
    }
});

and trying to recalling fetch function, what do you thing with this solution?

Thank you for the reply, @wisnup and @zulfikar18.

It’s hard for me to pinpoint the time when the fix will be released, however, I don’t think we can release a fix for this within this week. (Many of us, including me, are on leave from tomorrow until next week. It’s holiday here.)

What I first intended was that, if an exception is passed to the handler (which you passed to fetch), then you could retry like below.

final AtomicBoolean fetchSucceeded = new AtomicBoolean(false);
final int MAX_RETRY_COUNT = 3;
// try until fetch succeeds.
for (int trial = 0; trial < MAX_RETRY_COUNT; trial++) {
    if (fetchSucceeded.get()) break;
    final CountDownLatch fetchFinished = new CountDownLatch(1);
    channelCollection.fetch(new CompletionHandler() {
        @Override
        public void onCompleted(final SendBirdException e) {
            if (e != null) {
                fetchFinished.countDown();
                return;
            }
            fetchSucceeded.set(true);
            // TODO handle success
            fetchFinished.countDown();
        }
    });
    fetchFinished.await();
}

(I used the count down latch for explanation purposes but you don’t need to block current thread. the purpose is to keep calling the fetch until it succeeds, after the handler is called.)

If the exception is not passed to the handler, then I think you could make use of Thread.setDefaultUncaughtExceptionHandler, until we release a fix.

I’m not sure when these problem occur nor the internal status of the sync manager when the problem happens yet, so I’m not quite confident if above methods would work (for example fetch might keep failing if this is caused by some troublesome internal status error).

I’ll try to reproduce the error, and if you find a way that can reproduce this error, please let me know. After succeeding in the bug reproduction, I’ll post the workaround here. Thank you again for participating in the discussion.

The exception is not passed to the CompletionHandler. It cause our app to be force closed. That’s why we think of Thread.setDefaultUncaughtExceptionHandler. As for how to reproduce, we also don’t know, because sometimes it happens, sometimes it works.

@wisnup thank you for the reply. I think it will require some digging for me to find out what the root cause is. Please consider using Thread.setDefaultUncaughtExceptionHandler until we release a fix. Will update here if I make any progress or there’s something to share (ex: error was reproduced, possible workaround found, release is scheduled… etc). I’m sorry that this is the best I can share for now.

@Cjeon Thank you for your really fast response, as @wisnup said that it can be make our app become FC, so for now we will catching the exception until new version that fix this bug release, we will also update in here if we found any information about this issue

1 Like

@zulfikar18 sorry that I was not able to provide a quick fix. Thank you for your patience & understanding :grin:

1 Like

Hi @zulfikar18 and @wisnup. Thank you for waiting. The version that contains a possible fix (hopefully) for this is just released. Let me know if the new version fixes the bug! Please find the new version here.

Thank you for the fix (hopefully :blush:). We will update it and let you know whether the fix working or not.

1 Like

Thank you for the hard work @Cjeon, we will update it here for the result

1 Like