Skip to content

ecc-image-render-service

Cloud Run / CQS queue name: ecc-image-render-service Tech stack: Quarkus (Java), Gradle; Java2D (AWT) for image generation; GCS for LFS writes

ecc-image-render-service is the ECC-side image renderer. It subscribes to ecclink.v1 (and its aliases) as well as storeitemvalues.v1, fetches the referenced ECC model, parameters, and any custom fonts, and produces a rendered PNG for each ESL barcode, writing the output as ecceslimage.v1. It is one of two renderers whose outputs are merged by eslimage-merger; the other is the Studio renderer which produces studioeslimage.v1.

The service package structure reflects the dual heritage of the codebase: rendering logic lives under se.pricer.r3server (ported from Pricer Server), while the DtoFlow integration and CQS subscription management live under se.pricer.platform.

DTO Ownership

DTO Version Why
ecceslimage v1 Rendered PNG (stored in LFS) for a specific ESL barcode, keyed t/{tenantId}/s/{storeId}/e/{barcode}/p/{page}/ecceslimage; one record per ESL page; written after each successful render

CQS Subscriptions

DTO Version What the service does on change
ecclink v1 Primary trigger: fetch ECC model + item data, render image, write ecceslimage.v1
ecclink v1 (alias: by_storeesl) Unlink path: if the ecclink for a barcode is gone, delete all ecceslimage children for that ESL
storeitemvalues v1 Item data changed: look up all ecclink records for that item and re-render
eccfont v1 Font registry changed: loaded lazily on next render that needs the font (no explicit cache eviction)
eccmodel v1 Model definition changed: Caffeine cache eviction triggers re-fetch on next render (cache name: ecc-model-cache, max 100 entries)
eccparameters v1 Store-level ECC parameters changed: Caffeine cache eviction (cache name: ecc-parameters-cache, max 100 entries)

Processing Logic

Events arrive as batched DtoChangeNotification lists from the CQS subscriber. The service groups events by tenant+store before processing.

Per-store batch processing:

  1. Parse events: Classify each notification as an EcclinkV1 direct event, a by_storeesl alias event (unlink path), or a StoreItemValuesV1 event (item update path). Deduplicate by DTO ID, keeping the event with the latest SLA timestamp.

  2. Unlink path / cleanup via alias watch (by_storeesl alias): For each affected barcode alias, attempt to read the ecclink record; on NOT_FOUND, issue deleteChildren on the store-ESL parent — cascading to all ecceslimage page records for that ESL. Mechanic: this is an instance of the platform-level alias-watch cleanup pattern.

  3. Resolve Ecclinks:

  4. Direct ecclink.v1 events: batch-read the Ecclink DTOs.
  5. storeitemvalues.v1 events: use the by_item alias index to find all Ecclinks referencing the updated item.
  6. Deduplicate across both sources.

  7. Fetch item values: Collect all item IDs referenced by the resolved Ecclinks (single-item and multi-item variants); batch-read storeitemvalues.v1.

  8. Render (sequential per Ecclink to avoid AWT concurrency issues):

  9. Fetch eccmodel.v1 (Caffeine-cached) for the model ID carried in the Ecclink.
  10. Fetch eccparameters.v1 (Caffeine-cached) for the store.
  11. Map the ESL type ID to DeviceCapabilities (resolution, color depth, display type).
  12. Determine the IPF (Item Presentation Format) from the item's presentation property (defaults to "NORMAL").
  13. Select the matching ESLView from the model's price-view-per-IPF map.
  14. Run ESLFieldGenerator to evaluate data bindings, conversions, and rules — producing ESLFieldData.
  15. Load any custom fonts referenced by text-box shapes: fetch from eccfont.v1 (via DtoFlow gRPC), read the TTF bytes from LFS, register with AWT's GraphicsEnvironment. Font registration is cached in a ConcurrentHashSet to avoid redundant LFS reads.
  16. Call EslRenderService.generateImage() — Java2D rendering, returns a BufferedImage.
  17. Encode to PNG (ImageIO.write).

  18. Write to LFS and DtoFlow (parallel):

  19. Each rendered PNG is written to GCS asynchronously (virtual threads).
  20. Once all LFS writes succeed, a single putMany call commits all ecceslimage.v1 records to DtoFlow in one batch.

Rotation: The eccmodel.v1 record carries a rotation field (CW_90 or NONE). This is embedded in the ecceslimage.rotation field so that the downstream eslimage-merger and pricer-server know how to orient the image before sending it to hardware.

Error handling: If any single LFS write or render fails in a batch, the entire batch throws to allow CQS retry. Partial success is not committed.

  • ecclink-projector — upstream; produces ecclink.v1 from link.v2
  • item-registry — upstream; produces storeitemvalues.v1 from ERP/PIM data
  • eslimage-merger — downstream; merges ecceslimage.v1 with studioeslimage.v1eslimage.v1
  • dtoflow-lfs — LFS layer; GCS bucket dtoflow-lfs-eu-n1-{env} used for storing rendered PNGs