2 Replies Latest reply on Jul 14, 2010 12:53 PM by areohbee

    Catalog Access - Potential for Contention

    areohbee Level 5

      I have written a plugin that runs in the background and updates metadata in the catalog (periodically accessing for a very short time). If it can't access the catalog, it just sleeps for a while then tries again later - never complains...

       

      However, if it happens to be in the middle of accessing the catalog, other plugins that attempt to access the catalog may fail. For this reason, I have written the following function which solves catalog contention within my line of plugins. The moral of the story is that one should not assume another plugin is not already accessing the catalog...

       

      --[[
              Synopsis:           Catalog access wrapper that distinquishes catalog contention errors from target function errors.
             
              Notes:              - Returns immediately upon target function error.
            
                                  - The purpose of this function is so multiple concurrent tasks can access the catalog in succession without error.

       

                                  - It uses an ethernet collision avoidance technique for arbitration (retries after a short but random amount of time).

       

                                  tryCount is number of catalog contention errors to endure before involving the user.
                                  func is catalog with-do function
                                  cat is the catalog object.
                                  p1 is first parameter which may be a function, an action name, or a param table.
                                  p2 is second parameter which will be a function or nil.
             
              Returns:            itsAGoOrNot, andWhatever.
      --]]       
      function RcUtils.withCatalogDo( tryCount, func, cat, p1, p2 )
          while( true ) do
              for i = 1, tryCount do
                  local sts, qual = LrTasks.pcall( func, cat, p1, p2 )
                  if sts then
                      return true, qual
                  elseif RcString.is( qual ) then
                      local found = qual:find( "LrCatalog:with", 1, true ) or 0 -- return position or zero, instead of position or nil.
                      if found == 1 then -- problem reported by with-catalog-do method.
                          local found2 = qual:find( "already inside", 15, true )
                          if found2 then
                              -- problem is due to catalog access contention.
                              if RcUtils.debugMode then
                                  LrDialogs.message( 'cat contention: ' .. qual )
                              else
                                  LrTasks.sleep( math.random( .1, 1 ) ) -- sleep for a half-second or so, then try again.
                              end
                          else
                              -- LrDialogs.message( 'not already inside cat: ' .. RcLua.toString( qual ) )
                              return false, qual
                          end
                      else
                          -- LrDialogs.message( 'not a cat with msg: ' .. RcLua.toString( qual ) )
                          return false, qual
                      end
                  else
                      -- LrDialogs.message( 'bad cat sts, but no msg.' )
                      return false, 'Unknown error occurred accessing catalog.'
                  end
              end
              local action = RcUtils.showWarning( "Unable to access catalog.", "Keep Trying", "Give Up" )
              if action == 'ok' then
                  -- keep trying
                  -- LrDialogs.message( "Will try again." )
              else
                  -- assert( action == 'cancel', "unexpected error action: " .. RcLua.toString( action )  )
                  -- LrDialogs.message( "Gave up trying to access catalog." )
                  return false, "Gave up trying to access catalog."
              end
          end
          -- RcUtils.logError( "Unable to access catalog." ) - let this be done in calling context.
          -- LrDialogs.message( LOC( "$$$/X=Unable to access catalog after ^1 tries.", tryCount ) )
          return false, LOC( "$$$/X=Unable to access catalog." )
      end

        • 1. Re: Catalog Access - Potential for Contention
          Vladimir Vinogradsky

          You have obviously went to great lenghts to get this working, but what exactly is the purpose of constantly updating metadata in the background?

          • 2. Re: Catalog Access - Potential for Contention
            areohbee Level 5

            Hi Vladimir,

             

            You can see the outside & inside of DevMeta for complete details, but in a nutshell , there are few things the "background" task does, including:

             

            1. Check if the develop settings of the currently viewed photo have changed, if so update metadata for the library panel view (and Library Filter, and Smart Collections...) - this is what I call: Auto-Update.

            2. Check if user has edited the metadata - in which case it updates the develop settings of the active photo (called Live-Edit).

             

            The posted function makes sure none of my other plugins complain if they can't get at the catalog while DevMeta has it (which usually does not happen, but sometimes does). However, other peoples plugins will occasionally fail if they assume the catalog to be free for them all the time (and they have DevMeta running). There is at least one other fellow doing something similar, so I'm not the only plugin developer with the "bright idea" of doing things in the background that require catalog access.

             

            Rob