AnsweredAssumed Answered

Thread Safe AFEventFrame Recapture in AFSDK?

Question asked by halla on May 8, 2020
Latest reply on May 12, 2020 by halla

Is it possible to safely and effectively recapture Events in AFSDK using multiple threads?

I've created a c# .NET app that will simply iterate through all events retrieved from a search string and recapture and reevaluate the naming pattern for them.

This works great as a single threaded application, however, when I split the list up into chunks so that I can have multiple threads work on it I run into issues.

Multiple threads greatly increases performance but I find that it doesn't recapture/rename certain events even though the log shows that the event was processed?

 

I spoke with another developer and we thought it might be due to conflicting/simultaneous check-ins between threads so we tried wrapping the db.CheckIn() method in a 'lock' statement however this didn't seem to fix it.

The threads are each working on a separate list of events (no duplication between lists).

 

Client Version: 2018 SP2

Server Version: 2017 R2

 

Am I missing something?

        public static void RecaptureEventFrames(AFDatabase db, string efSearchQuery, string startTime, string endTime,
                                        bool captureMode = true,
                                        bool reEvaluateNamingPattern = true,
                                        bool forceUndoCheckOut = true,
                                        bool deleteMode = false,
                                        int numThreads = 2)
        {
           
            db.Refresh();
            AFTime start, end;
            AFEventFrameSearch search;
            List<AFEventFrame> efs;
            try
            {
                //Find Events
                start = new AFTime(startTime);
                end = new AFTime(endTime);
                search = new AFEventFrameSearch(db, "EFSearch", AFSearchMode.Inclusive, start, end, efSearchQuery);
                search.Refresh();
                efs = search.FindEventFrames().ToList();
                //New way
                //efs = search.FindObjects().ToList();
            }
            catch (Exception e)
            {
                _log.Error(e);
                return;
            }

            //Break up events into chunks for parallel processing
            int chunks = efs.Count() / numThreads;
            chunks += 1;
            var cntEfs = efs.Count();
            _log.Debug($"Starting of the iteration of event frames:");
            var efLists = efs.ChunkBy(chunks);


            //global event counter
            var globalCount = 0;

            _log.Debug($"MODE SETTINGS: Delete:{deleteMode} Capture:{captureMode} reEvaluateNamingPattern:{reEvaluateNamingPattern} forceUndoCheckout:{forceUndoCheckOut}");


            List<Task> tasks = new List<Task>();
            foreach (var efList in efLists)
            {

                //Multi-thread each child list
                var t = Task.Factory.StartNew(() =>
                {
                    var threadId = Thread.CurrentThread.ManagedThreadId;
                    #region EF Iteration/Check-In
                    int count = 0;
                    foreach (var ef in efList)
                    {

                        if (captureMode)
                        {
                            var elem = ef.PrimaryReferencedElement;
                            if (elem == null) continue;
                            _log.Debug($"Capturing Values for|{ef.Name}|{ef.StartTime}|{ef.EndTime}");                           
                            ef.CaptureValues();
                        }

                        if (reEvaluateNamingPattern)
                        {
                            var oldName = String.Copy(ef.Name);
                            AFNameSubstitution.ResolveName(ef);
                            var newName = ef.Name;
                            _log.Debug($"Reevaluated EF. oldName={oldName}|newName={newName}");
                        }

                        //Log simple names to a file
                        _logNames.Info($"{ef.Name}|{ef.StartTime}|{ef.EndTime}");

                        Interlocked.Increment(ref globalCount);
                        count++;
                       
                        //Checkin every nth iteration
                        if (count % 100 == 0)
                        {
                            _log.Info($"Checking in changes! Count = {count}");
                            try
                            {                               
                                lock (lockObject)
                                {                                   
                                    db.CheckIn(AFCheckedOutMode.ObjectsCheckedOutThisThread);
                                }
                            }
                            catch (Exception e)
                            {
                               
                                _log.Error(e, e.Message);
                            }

                            Thread.Sleep(300);
                        }

                    }
                    if (count > 0)
                    {
                        _log.Info($"[{threadId}] - Checking in FINAL changes! Total Count = {count}");
                        try
                        {
                            db.CheckIn(AFCheckedOutMode.ObjectsCheckedOutThisThread);
                        }
                        catch (Exception e)
                        {
                            _log.Error(e, e.Message);
                        }
                    }
                    else
                    {
                        _log.Info($"No changes needed!");
                    }
                    #endregion
                });

                tasks.Add(t);

                //Wait a bit to launch the next thread
                Thread.Sleep(3000);

            }

            _log.Info($"Waiting on all threads to stop..", ConsoleColor.DarkGray);
            try
            {
                var nonNullTasks = tasks.Where(t => t != null);
                Task.WaitAll(tasks.ToArray());
            }
            catch (Exception e)
            {
                _log.Error(e, e.Message);
            }

            //Check in everything just in case?
            _log.Info($"Checking in everything for the current user...");
            db.CheckIn(AFCheckedOutMode.ObjectsCheckedOutThisSession);

            _log.Info($"All tasks completed! Total events modified: {globalCount}");
           
            Console.WriteLine("EventFrame Iteration complete");
        }

Outcomes