PhoenixKit.Modules.Storage.Reconciler (phoenix_kit v2.41.0)

Copy Markdown View Source

Makes each file match its library's storage profile and variant set (V205, plan ยง6.3). Storage.Workers.ReconcileJob runs it.

A file records the profile and the variant set (each at a revision) it was last placed by (placed_profile_uuid / placed_revision, placed_variant_set_uuid / placed_variant_revision; NULL is the Default at revision 1). It is stale (stale_query/0) when either differs from what its library uses now: the library moved to another profile or set, one of them changed, or an upload made fewer copies or sizes than wanted (which stamps revision 0).

For a stale file, reconcile_file/1:

  • locations (G4, G5, G6) โ€” for each completed instance: an original upload is an :original, everything else (sizes, tiles, renders) is :derived (G13; an edit's hidden backup of the unedited original is an original). Its good copies are on the profile's buckets that store that kind and are active or read_only. While there are fewer than the profile wants (capped at the buckets it can use), the object is copied to more of its writable buckets and each copy is checked before it counts. Once enough are good, copies on buckets the profile no longer uses for it (not listed, draining, storing the other kind) are unlinked: the location row goes, and the object on that bucket only when nothing else there names the key (G11, Storage.unlink_location/2).
  • variants (G15, G16) โ€” sizes of the set the file lacks are made, those whose spec_hash differs from the size's spec now are made again, and those the set no longer has (a size deleted from it; a disabled size is kept) are deleted.

A file that ends up matching both is stamped with what it now matches, at the revisions read when it was started (a change made meanwhile leaves it stale for the next pass). A failure leaves it stale; the next pass tries again. Instances not yet checked by the location backfill are skipped (their buckets are not known), and so is a file still uploading or with an image edit pending.

Summary

Functions

Whether any file is stale.

Makes file match its library's profile and variant set, and stamps it when it does. Returns :reconciled, :stale (something could not be done yet; it stays stale) or :skipped (another reconciler holds it). One reconciler at a time per file, across nodes: a session advisory lock on the file, held while its bytes are copied.

Reconciles up to limit stale files after cursor (a file uuid; nil from the start), in uuid order. Returns {:more, last_uuid, totals} while a full batch was read, {:done, totals} once the walk has passed the last one; totals counts files :reconciled, :stale (still) and :skipped.

How many files are stale.

Up to limit stale files for the Health page: the file, its library's name, and whether its copies (:placement) or its sizes (:variants) are what is out of date.

The files that are not where, or not what, their library's profile and variant set want. Not a file an upload is still processing, nor one with an image edit pending, nor one tried in the last few minutes.

How many files the reconciler looks after (active and trashed ones).

Functions

pending?()

@spec pending?() :: boolean()

Whether any file is stale.

reconcile_file(file)

@spec reconcile_file(PhoenixKit.Modules.Storage.File.t()) ::
  :reconciled | :stale | :skipped

Makes file match its library's profile and variant set, and stamps it when it does. Returns :reconciled, :stale (something could not be done yet; it stays stale) or :skipped (another reconciler holds it). One reconciler at a time per file, across nodes: a session advisory lock on the file, held while its bytes are copied.

run_batch(cursor, limit, totals \\ %{})

@spec run_batch(String.t() | nil, pos_integer(), map()) ::
  {:more, String.t(), map()} | {:done, map()}

Reconciles up to limit stale files after cursor (a file uuid; nil from the start), in uuid order. Returns {:more, last_uuid, totals} while a full batch was read, {:done, totals} once the walk has passed the last one; totals counts files :reconciled, :stale (still) and :skipped.

stale_count()

@spec stale_count() :: non_neg_integer()

How many files are stale.

stale_files(limit \\ 200)

@spec stale_files(pos_integer()) :: [map()]

Up to limit stale files for the Health page: the file, its library's name, and whether its copies (:placement) or its sizes (:variants) are what is out of date.

stale_query()

@spec stale_query() :: Ecto.Query.t()

The files that are not where, or not what, their library's profile and variant set want. Not a file an upload is still processing, nor one with an image edit pending, nor one tried in the last few minutes.

total_count()

@spec total_count() :: non_neg_integer()

How many files the reconciler looks after (active and trashed ones).