Skip navigation
Currently Being Moderated

Functions for multi-phase catalog access, with retries...

May 24, 2012 5:23 AM

Tags: #with #catalog #withprolongedwriteaccessdo #contention #withwriteaccessdo #withprivatewriteaccessdo

See Catalog:action & Catalog:privateAction below.

 

These functions support iterative catalog access, to divide catalog updates into chunks...

 

Also, they support retries to deal with contention in case another plugin task is already accessing the catalog.

 

 

-- private method:

function Catalog:_isAccessContention( qual )

    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 == nil then

            found2 = qual:find( "was blocked", 15, true ) -- Lr4b

        end

        if found2 then

            -- problem is due to catalog access contention.

            Debug.logn( 'catalog contention:', str:to( qual ) )

            return true

        else

            return false

        end

    else

        return false

    end

end

 

 

 

-- private method:

function Catalog:_action( catFunc, tmo, name, func, ... )

    local t = { ... }

    local tries = math.max( math.ceil( tmo * 2 ), 1 )

    local sts, msg

    local function _func( context )

        sts, msg = func( unpack( t ) )

    end

    local count = 10000000 -- ten million max, for sanity.

    repeat

        local s, other

        if name then

            s, other = LrTasks.pcall( catFunc, catalog, name, _func )

        else

            s, other = LrTasks.pcall( catFunc, catalog, _func )

        end

        if s then -- no error thrown

            if sts then

                return true -- all done.

            elseif str:is( msg ) then

                return false, "Unable to complete catalog update - " .. msg

            else

                -- continue

                count = count - 1                       

            end

        else

            if self:_isAccessContention( other ) then

                tries = tries - 1

                if tries == 0 then

                    return nil, "Unable to access catalog for " .. str:to( tmo ) .. " seconds."

                else

                    LrTasks.sleep( math.random( .1, 1 ) ) -- sleep for half second, plus or minus.

                end

            else

                return nil, "Catalog access function error: " .. str:to( other )

            end

        end

    until count == 0

    return nil, "Program failure"

end

 

 

 

--- Wrapper for named/undoable catalog:withWriteAccessDo method - divide to conquor func.

--

--  @param tmo (number) max seconds to get in.

--  @param name (string) undo title.

--  @param func (function) divided catalog writing function: returns sts, msg = true when done; false if to be continued; nil, error message if trouble.

--  @param ... (any) passed to func - often a table containing photo start index.

--

--  @usage example:<br>

--           local function catalogAction( t )<br>

--               if t.i > #photos then<br>

--                   return true -- done, no errors (although should pre-check for photos to process).<br>

--               else<br>

--                   local k = math.min( t.i + 1000, #photos )<br>

--                   while ( t.i <= k ) do<br>

--                       -- do something to photos[t.i]<br>

--                       -- if trouble, return nil, msg.<br>

--                       t.i = t.i + 1

--                   end<br>

--                   if t.i > #photos then<br>

--                       return true -- done, no errors.<br>

--                   else<br>

--                       return false -- continue, no errors.<br>

--                   end<br>

--               end<br>

--           end<br>

--           local sts, msg = cat:action( 10, "Test", catalogAction, { i=1 } )<br>

--           if sts then<br>

--               -- log successful message.<br>

--           else<br>

--               -- print error message...<br>

--           end<br>

--

function Catalog:action( tmo, name, func, ... )

    return self:_action( catalog.withWriteAccessDo, tmo, name, func, ... )

end

 

 

 

--- Wrapper for un-named catalog:withPrivateWriteAccessDo method - divide to conquor func.

--

--

--  @param tmo (number) max seconds to get in.

--  @param func (function) divided catalog writing function: returns sts, msg = true when done; false if to be continued; nil, error message if trouble.

--  @param ... (any) passed to func.

--

function Catalog:privateAction( tmo, func, ... )

    return self:_action( catalog.withPrivateWriteAccessDo, tmo, nil, func, ... ) -- no name.

end

 

 

Note: These functions were changed a bit after further testing and integration - consider them for example purposes only. The released version of these functions will be available shortly as part of the Elare Plugin Framework:

 

https://www.assembla.com/spaces/lrdevplugin/

 

PS - After some mods, the final version allows me to write code like this which I find convenient (still not released yet at assembla.com):

 

local coll

local s, m = cat:update( 10, "Assure Folder Collection", function( context, phase )

    if phase == 1 then

        coll = catalog:createCollection( folder:getName(), parent, true )

        return false -- keep going.

    elseif phase == 2 then

        coll:removeAllPhotos()

        return false -- keep going.

    elseif phase == 3 then

        coll:addPhotos( folder:getPhotos() )

        return true -- done (same as returning nil).

    else

        app:error( "bad phase" )

    end

end )

if s then

    app:logVerbose( "Assured collection: ^1", coll:getName() )

    return coll

else

    return nil, m

end

 

or

 

local photos = catalog:getAllPhotos()

local s, m = cat:update( 10, "Update Photos", function( context, phase )

     local i1 = (phase - 1) * 1000 + 1

     local i2 = math.min( phase * 1000, #photos )

     for i = i1, i2 do

        local photo = photos[i]

        -- do something with photo that writes catalog.

    end

    if i2 < #photos then

        return false -- keep going

    end

end )

if s then

    app:logVerbose( "Photos updated..." )

    return true

else

    return false, m

end

 

More Like This

  • Retrieving data ...

Bookmarked By (0)