[AZ-284] Autodev baseline + testability refactor

Phase A baseline outputs from /autodev (Steps 1-5):
- Problem & solution docs (_docs/00_problem, _docs/01_solution)
- Codebase documentation (_docs/02_document) incl. architecture,
  module-layout, glossary, system-flows, baseline compliance scan
- Test specs (blackbox, performance, resilience, security, resource,
  traceability matrix)
- Test task decomposition (_docs/02_tasks/todo): AZ-285..AZ-290
- Testability refactor (_docs/04_refactoring/01-testability-refactoring):
  - TC-01 Move DownloadedTileInfoV2 + new ExistingTileInfo to Common.DTO
  - TC-02 Replace dead ISatelliteDownloader API with real signatures
  - TC-03 GoogleMapsDownloaderV2 implements ISatelliteDownloader
  - TC-04 TileService depends on ISatelliteDownloader (mockable)
  - TC-05 DI + endpoints use ISatelliteDownloader
- Test runner scripts (scripts/run-tests.sh, run-performance-tests.sh)
- Autodev state pointer (_docs/_autodev_state.md)

Prepares the codebase for AZ-285..AZ-290 unit/integration test work.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-10 04:44:08 +03:00
parent 25a644a9bf
commit b0fffa6d42
68 changed files with 4192 additions and 11 deletions
@@ -0,0 +1,91 @@
# Module: Services/RegionService + RegionProcessingService + RegionRequestQueue
## Purpose
End-to-end region processing pipeline: API request handling → queue → background worker → tile download → output file generation. Three closely coupled classes form the region processing subsystem.
## Public Interface
### RegionService (implements IRegionService)
- `RequestRegionAsync(...)`: creates `RegionEntity` with status "queued", enqueues a `RegionRequest`, returns `RegionStatus`
- `GetRegionStatusAsync(Guid id)`: reads region record and maps to `RegionStatus`
- `ProcessRegionAsync(Guid id, CancellationToken)`: the main processing pipeline — see Internal Logic
### RegionProcessingService (BackgroundService)
- `ExecuteAsync(CancellationToken)`: spawns `MaxConcurrentRegions` parallel worker tasks, each in an infinite dequeue loop
### RegionRequestQueue (implements IRegionRequestQueue)
- `EnqueueAsync(RegionRequest, CancellationToken)`: writes to a bounded `Channel<RegionRequest>`
- `DequeueAsync(CancellationToken)`: reads from the channel (blocks until available)
- `Count`: current queue depth
## Internal Logic
### RegionService.ProcessRegionAsync
1. Sets region status to "processing"
2. Creates a 5-minute timeout `CancellationTokenSource`
3. Queries existing tiles in the region
4. Calls `TileService.DownloadAndStoreTilesAsync` to fetch missing tiles
5. Counts downloaded vs reused tiles
6. Generates CSV file (`region_{id}_ready.csv`) listing tile coordinates + paths
7. Optionally stitches tiles into a single JPEG image (if `StitchTiles` is true)
8. Generates summary file (`region_{id}_summary.txt`)
9. Updates region status to "completed"
10. On any error: sets status to "failed", generates error summary
### Tile Stitching
Uses ImageSharp to:
1. Compute a tile grid from tile coordinates
2. Create a new image of `(gridWidth × 256) × (gridHeight × 256)` pixels
3. Place each tile image at its grid position
4. Draw a red crosshair at the center coordinates
5. Save as JPEG
### Error Handling
Comprehensive catch blocks for:
- `TaskCanceledException` (timeout vs external cancellation)
- `OperationCanceledException`
- `RateLimitException` (Google rate limiting)
- `HttpRequestException` (with status code)
- Generic `Exception`
Each sets status to "failed" and writes an error summary file.
### RegionProcessingService
- Spawns `MaxConcurrentRegions` worker tasks with staggered startup (100500ms random delay)
- Each worker loops: dequeue → `ProcessRegionAsync` → repeat
- Graceful shutdown on cancellation
### RegionRequestQueue
- Uses `System.Threading.Channels.Channel<T>.CreateBounded` with `BoundedChannelFullMode.Wait`
- Tracks `_totalEnqueued` and `_totalDequeued` counters
## Dependencies
- `ITileService`, `IRegionRepository`, `IRegionRequestQueue`
- `StorageConfig`, `ProcessingConfig`
- `SixLabors.ImageSharp` — tile stitching
- `SatelliteProvider.Common.Utils.GeoUtils` — coordinate conversion for stitching
## Consumers
- `Program.cs` API endpoints — `RequestRegionAsync`, `GetRegionStatusAsync`
- `RouteService``RequestRegionAsync` (for geofence regions)
- `RouteProcessingService``RequestRegionAsync` (for route-point regions)
## Data Models
- Input: `RegionRequest` (queue message)
- Output: `RegionStatus` (API response), CSV files, summary files, stitched images
- Persistence: `RegionEntity`
## Configuration
- `StorageConfig.ReadyDirectory` — output file location
- `ProcessingConfig.MaxConcurrentRegions` — worker count
- `ProcessingConfig.QueueCapacity` — bounded channel size
## External Integrations
- PostgreSQL (via repositories)
- File system (CSV, summary, stitched images in `./ready/`)
- Google Maps (indirectly via TileService → GoogleMapsDownloaderV2)
## Security
None.
## Tests
Integration tests in `RegionTests.cs` cover the request → poll → complete flow.