feat: full layer deletion flow (MAPCO-7285)#107
Conversation
…ngTaskTypes (MAPCO-7285)
…nd package-lock.json
…-7285) The tiles-deletion cleaner task now carries the layer's storage path from the mapproxy cache directory instead of the catalogId: - MapproxyApiClient.getS3CacheBucketName -> getLayerCache, returning the full cache (directory + bucket) for the configured cache type, undefined on 404. - DeleteLayerHandler resolves the tiles location from the cache and persists it into the task params before any deletion step, so a redelivered task survives the deleteFromMapproxy step that destroys the cache. S3 drops the leading slash (object keys are lstripped); FS drops the first segment (the mapproxy PVC mount) so the Cleaner can rejoin its own base path. - Refactor the handler for readability: data-driven deletion-step loop, single telemetry object, and extracted toRelativeTilesPath / resolveTilesBucket.
| activeSpan?.setStatus({ code: SpanStatusCode.OK, message: 'Catalog record deleted successfully' }); | ||
| } catch (err) { | ||
| if (err instanceof NotFoundError) { | ||
| this.logger.warn({ msg: 'catalog record not found, treating as already deleted', catalogId }); |
There was a problem hiding this comment.
| this.logger.warn({ msg: 'catalog record not found, treating as already deleted', catalogId }); | |
| this.logger.warn({ msg: 'catalog record not found, skipping, catalogId }); |
| } catch (err) { | ||
| if (err instanceof NotFoundError) { | ||
| this.logger.warn({ msg: 'catalog record not found, treating as already deleted', catalogId }); | ||
| activeSpan?.setStatus({ code: SpanStatusCode.OK, message: 'Catalog record already absent' }); |
There was a problem hiding this comment.
use the same message from above warn log
| } catch (err) { | ||
| if (err instanceof NotFoundError) { | ||
| // already gone — unpublish is idempotent, a 404 on (re)run is success (§6) | ||
| this.logger.warn({ msg: 'Layer feature type not found in geoserver, treating as already unpublished', layerName }); |
There was a problem hiding this comment.
| this.logger.warn({ msg: 'Layer feature type not found in geoserver, treating as already unpublished', layerName }); | |
| this.logger.warn({ msg: 'Layer feature type not found in geoserver, skipping', layerName }); |
| if (err instanceof NotFoundError) { | ||
| // already gone — unpublish is idempotent, a 404 on (re)run is success (§6) | ||
| this.logger.warn({ msg: 'Layer feature type not found in geoserver, treating as already unpublished', layerName }); | ||
| activeSpan?.setStatus({ code: SpanStatusCode.OK, message: 'Layer already absent in geoserver' }); |
There was a problem hiding this comment.
use same message from above warn log
| activeSpan?.setStatus({ code: SpanStatusCode.OK, message: 'Layer removed successfully from mapproxy' }); | ||
| } catch (err) { | ||
| if (err instanceof NotFoundError) { | ||
| this.logger.warn({ msg: 'layer not found in mapproxy, treating as already removed', layerName }); |
There was a problem hiding this comment.
| this.logger.warn({ msg: 'layer not found in mapproxy, treating as already removed', layerName }); | |
| this.logger.warn({ msg: 'layer not found in mapproxy, skipping', layerName }); |
| activeSpan?.setStatus({ code: SpanStatusCode.OK, message: 'Layer already absent in mapproxy' }); | ||
| return; | ||
| } | ||
| if (err instanceof DeleteLayerError) { |
There was a problem hiding this comment.
those two block of error condition seems to have a similar behavior can we merge it into a single error throw?
There was a problem hiding this comment.
call the getSteps() earlier to reuse it instead duplicated calls
| private async resolveTilesLocation(layerName: LayerName): Promise<TilesLocation> { | ||
| const cache = await this.mapproxyClient.getLayerCache(layerName); | ||
| const directory = cache?.cache.directory; | ||
| if (directory === undefined) { |
There was a problem hiding this comment.
why are we depends on the "directory" to throw LayerCacheNotFoundError?
instead we can check if the cache const is undefined as fetchLayerCache returns undefined in case of no NotFound error from mapproxy-api
| throw new LayerCacheNotFoundError(layerName, this.tilesStorageProvider); | ||
| } | ||
|
|
||
| const path = this.toRelativeTilesPath(directory); |
There was a problem hiding this comment.
above change will make it irrelevant
| ? { storageProvider: this.tilesStorageProvider, bucket: tilesLocation.bucket ?? this.tilesBucketConfig } | ||
| : { storageProvider: this.tilesStorageProvider }; | ||
|
|
||
| const tilesDeletionTask: ICreateTaskBody<DeleteStoredResourcesParams> = { |
There was a problem hiding this comment.
set the blockDuplication task to truek and get rid of the "tiles-deletion" task exists check on the top of the function
Related issues: MAPCO-7285
Overview
Implements the full-layer deletion flow in the overseer. A
Delete_Layerjob with adeletetask drives ordered removal of a layer's metadata across every system that references it, then hands tile (and, later, artifact) cleanup off to the Cleaner via a downstreamtiles-deletiontask.What this adds
Per-client deletion methods
CatalogClient.deleteRecord— remove the catalog record.GeoserverClient:unpublishLayer→ performs the geoserver unpublish.PolygonPartsMangerClient.deleteEntities— drop the polygon-parts entities.MapproxyApiClient.removeLayer— remove the layer from mapproxy (treats a 404 as already-removed / idempotent).Storage / cache plumbing
artifactsBucketandtilesBucket(helm + config), guarded bytilesStorageProvider.MapproxyApiClient.getLayerCachereturns the layer's cache object (directory + bucket) for the configured cache type, orundefinedon 404.DeleteLayerHandler(Delete_Layer/delete)deleteFromMapproxystep destroys the cache, so a redelivered task reads the location from its own params instead of re-querying a layer that no longer exists.directory(not the catalogId): S3 drops the leading slash (object keys are lstripped by mapproxy), FS drops the first segment (the mapproxy PVC mount) so the Cleaner rejoins its own base path. S3 also carries the bucket (cachebucket_name, falling back to configuredtilesBucket).tiles-deletioncleaner task once all metadata steps complete, with an idempotency guard against duplicate creation on redelivery.Wiring
Delete_Layerjob anddeletetask in the polling config and DI container.configUtil: extracted per-domain polling-task-type selection intogetPollingTaskTypes.Notes / follow-ups
TODOin the handler): mapproxy'sGET /layerdoes not expose artifacts, so the existence source-of-truth and params shape are unresolved.Tests
getLayerCache, and the fullDeleteLayerHandlerflow (step ordering + per-step persistence, redelivery skip, FS/S3 path + bucket resolution, cleaner-task idempotency, and unresolvable-cache rejection).tsc --noEmit, eslint, and prettier clean.