Closes #145 - #209
Closes #145#209lcalisto wants to merge 5 commits into
Conversation
ricardogsilva
left a comment
There was a problem hiding this comment.
This is a very commendable effort.
Unfortunately, it seems based on a different assumption than the one that drove the original design - this is maybe my fault for not having made it clearer. I apologize for that 🥲
Letting the generation of derived assets run together with the discovery was meant to mean that the discovery runs first and then it automatically triggers an event to kickstart the derivation - it was not meant to mean that derivation would run intertwined with the discovery. Having discovery and derivation being two independent processes means that we can call any of them in isolation, if needed.
I'm requesting a change, but I'd also be open to changing my mind, if you can convince me that this is a better way than the original plan 😉 - let me know your thoughts
There was a problem hiding this comment.
I'd have this configuration moved to the asset discovery configuration model. Not necessarily on this PR, but we will need to do it at some point.
Also a very minor nitpick - I would prefer the term 'directory' instead of 'folder', in order to keep it homogeneous with the rest of the code. The term folder is more of a GUI-related metaphor, and seems less appropriate here - I don't mean that this needs to be changed for this PR to be merged, it is just a minor note
There was a problem hiding this comment.
Renamed to directory. Moving it into the asset discovery configuration model. Fully agree, issue #213
There was a problem hiding this comment.
There is no bind volume for the config directory here - does this mean we have nothing in CI that needs it? Would it also mean that we don't need this additional previews-worker service?
If we don't need this service in CI, perhaps we can not define it, in order to conserve resources?
There was a problem hiding this comment.
Right on both the worker is gone from the CI compose. The config bind is not needed there , the file goes inside the image.
There was a problem hiding this comment.
There is no definition of the volume bind for config here either. In this case it seems this would be required for the production deployment to work appropriately.
There was a problem hiding this comment.
Not needed. the Dockerfile copies the whole thing, prod reads the copied file. It can still be overridden per environment via SEIS_LAB_DATA__PREVIEW_DIRECTORIES_PATH.
| # extra args are passed straight to dramatiq, which is how a worker gets | ||
| # pinned to a queue (`run-processing-worker --queues previews`) |
There was a problem hiding this comment.
This should be part of the docstring.
| Two discovery runs may ask for previews of the same record at the same time, | ||
| and interleaved deletes and inserts would then commit duplicates, so the | ||
| replacement is serialized per record with an advisory lock and done in a | ||
| single transaction. | ||
| """ | ||
| await session.execute( | ||
| text("SELECT pg_advisory_xact_lock(hashtext(:record_id))"), | ||
| {"record_id": str(survey_related_record.id)}, | ||
| ) |
There was a problem hiding this comment.
I'm not sure we need to reach for pg's explicit locking mechanism.
We already have the constants.SurveyRelatedRecordStatus.UNDER_DERIVATION status, which can be set on the record - This is supposed to prevent two simultaneous derivation operations on the same record.
I imagine that the derivation process would start by checking the record's status property and refuse to proceed unless it had a value of either DRAFT or PUBLISHED.
Maybe you looked into this and have a different opinion though?
There was a problem hiding this comment.
Lock removed. The flip is a single conditional UPDATE, held only around the write of the derived assets, so a worker killed mid-render cannot strand a record in UNDER_DERIVATION.
| # they become stale as soon as those change; the advisory lock keeps an | ||
| # in-flight preview task from re-inserting them after this delete | ||
| await session.execute( | ||
| text("SELECT pg_advisory_xact_lock(hashtext(:record_id))"), | ||
| {"record_id": str(survey_related_record.id)}, | ||
| ) |
There was a problem hiding this comment.
As mentioned above, I'd prefer to not resort to using pg explicit locks unless absolutely necessary.
I'm thinking that the operations layer, (which is where most coordination logic should be living) needs to check the current status of a record before calling the db command. If a record is not currently in either DRAFT or PUBLISHED state, then it would simply not be possible to have it be updated. This was the original plan anyway.
The original idea was to have asset derivation be an additional task, after discovery had already been done. We may need to rethink this now.
There was a problem hiding this comment.
Removed as well ,the update operation now refuses records that are not DRAFT or PUBLISHED
| # re-running discovery is the recovery path for records whose | ||
| # previews are missing, so only those get enqueued again | ||
| if deriver_dispatch.is_previewable( | ||
| str(found_path), relative_file_path, preview_folders | ||
| ): | ||
| existing_record_id = identifiers.SurveyRelatedRecordId( | ||
| existing_asset.survey_related_record_id | ||
| ) | ||
| has_derived_assets = any( | ||
| constants.AssetType.DATA not in asset.asset_type | ||
| for asset in await asset_queries.collect_all_record_assets( | ||
| session, existing_record_id | ||
| ) | ||
| ) | ||
| if not has_derived_assets: | ||
| preview_tasks.generate_record_previews.send( | ||
| raw_request_id=str(request_id), | ||
| raw_survey_related_record_id=str(existing_record_id), | ||
| raw_initiator=json.dumps(dataclasses.asdict(user)), | ||
| ) |
There was a problem hiding this comment.
The original plan was to have discovery decoupled from aux asset derivation, as these are two conceptually different operations. Even if we may perform derivation automatically, after discovery, like you mention in the PR description, the two should still be different and individually addressable operations.
I imagine that after discovery is finished there would be something like a DISCOVERY_FINISHED event which is sent and which would trigger a handler process to then start the derivation process.
There was a problem hiding this comment.
Reworked as sugested. discovery's final step enqueues one mission-level derivation task, and the derivation operation is independently callable.
| return frozenset(json.loads(contents)["folders"]) | ||
|
|
||
|
|
||
| async def generate_record_previews( |
There was a problem hiding this comment.
As mentioned before, this operation needs to check the record's current status in order to know whether to allow mutation of the record
There was a problem hiding this comment.
Done! operation skips records that are not DRAFT or PUBLISHED
Thanks for the great review @ricardogsilva ! Agree with most of what you raised and no need to apologize, I'm really happy with what we are achieving!
|
Closes #145
First version of automatic preview generation, hooked into discovery
New
tasks/derivers/package (mirrorstasks/extractors/) renders a raster into a small WEBP (warped to 4326, percentile stretch, nodata as transparency), stored on the record as a derived asset (asset_type={THUMBNAIL,PREVIEW}, bytes indata, bounds ingeog, norelative_path) via theDerivedRecordAssetCreateschema from Closes #175 #192Discovery enqueues generation on a dedicated
previewsdramatiq queue (new compose worker; the existing one is now pinned todefault). Enqueue is missing-only, the task is replace-all under a per-record advisory lock, so generation is idempotent and re-running discovery is also the recovery pathEligibility = prefix match against
config/preview-folders.json( real folder list from IPMA) .This v1 covers rasters that declare their own CRS (
.tif/.tiff). Still to be done next: viewer that actually shows these previews (#154), then more formats as separate issues: XYZ via the mission'simplicit_crs, vector via Martin ??, KMALL, and SEG-Y (just an icon, still to be decided).Compose stacks need to gain a
previews-workerservice.Note: the issue text says derived datasets are not to be generated automatically. In the July meeting we decided generation is automatic, hooked into discovery so users dont need to manage it. The task is trigger agnostic (idempotent), so a manual button can still be added later if a use case appears.