Skip to content

Releases: hlindset/image_pipe

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 13 Oct 14:55
4107245

image_pipe_url

Removed

  • Breaking: ImagePipe.Plan.Source.Path, ImagePipe.Plan.Source.URL, and
    ImagePipe.Plan.Source.Object. They are now ImagePipe.Source.Path,
    ImagePipe.Source.URL, and ImagePipe.Source.Object in image_pipe, whose
    custom source adapters receive them.
  • The dependency on mime. The package depends only on nimble_options and
    color at runtime.

image_pipe

Added

  • SVG sources, when the optional image_pipe_svg package is installed.
    An SVG renders straight at the size the request needs, so it stays sharp
    at any size, and a resize can make it larger than its own width and
    height without enlarge, up to the input pixel limit, past which the
    rendered image is enlarged. Regions and crops in pixels measure the SVG at
    its own size. With no format and no negotiated format, the result is a
    PNG. Watermark images can be SVGs too, and render at the size they're
    drawn. Without image_pipe_svg, SVG sources still fail with 415.
  • The :svg option configures SVG sources: :font_dirs and
    :fallback_family for the fonts SVG text renders in (no system fonts are
    used), and :max_bytes, :max_render_work, and :max_render_pixels to
    limit the work an SVG can cause.
  • A [:source, :svg_render] span wraps rendering an SVG original or
    watermark, with its intrinsic and rendered sizes and the estimated work,
    and names the limit that rejected an SVG. The default Logger logs it.
  • cache and input_cache delete metadata files they can't read, such as
    ones another ImagePipe version wrote, and their bodies, once the files are
    more than an hour old. The [:cache, :sweep] event reports them as
    :unreadable.
  • [:cache, :stage] reports writes the cache drops to keep up with its
    storage as :stage_dropped, for processed images and originals, and
    carries :pool. The default Logger logs dropped writes at :warning.
  • [:cache, :coordination] carries reason: :deadline when a request stops
    waiting for another request's image because it wasn't stored in time.
  • The image_pipe.request span carries a request_id attribute when Logger
    metadata has a :request_id, such as the one Plug.RequestId sets.
  • The [:cache, :lookup], [:cache, :write], and [:cache, :stage]
    telemetry events carry :cache_key, the hash of the entry's cache key, and
    [:cache, :lookup] carries :entry, which says whether it read a processed
    response or a source record. The tracer adds them to their spans and span
    events.
  • The [:cache, :write] stop event carries :estimated_cost_us, the work
    the cache estimates it took to make a processed image. The tracer adds it
    to the span.
  • The [:cache, :write] stop event carries :bytes, the size of the stored
    body, so dividing it by the span's duration gives a rough write speed for the
    cache storage. [:cache, :lookup] carries :bytes on a hit for a processed
    response. The tracer adds :bytes to both spans, and the default Logger
    logs it.
  • A [:source, :decode_open] span wraps each time libvips opens the original,
    so a trace shows how often a request opens it. The default Logger doesn't log
    it.

Changed

  • With image_pipe_svg installed, cache keys and ETags include the resvg
    version, the fonts, and the fallback family, so every cached image misses
    once after installing it, and again after changing the fonts or fallback
    family, or upgrading image_pipe_svg to a version that renders SVGs
    differently.
  • Breaking: An HTTP source's allowed_hosts entry without a port allows
    only the scheme's default port, 80 or 443, including for redirect targets.
    Before, any port on a listed host was reachable. Add an entry with the port,
    such as "assets.example.com:8443", for an origin on another port. A
    base_url with a non-default port needs that port in allowed_hosts when you
    set the list. A malformed entry, such as "assets.example.com:https", now
    fails at startup.
  • A response to a URL with expires drops stale-while-revalidate and
    stale-if-error, including ones the host sets, because caches can't use them
    alongside its must-revalidate.
  • Processed images and originals cached by an earlier version are not
    reused, because the cache key changed and entry files are named
    differently. The cache refills as requests come in, and deletes the old
    files as
    leftover files.
  • Requests for PNG and other sources that need no shrink-on-load open the
    source once instead of twice, which saves about 0.3 ms per request.
  • A custom detector's identity/1, available?/1, and ready?/1 receive only
    :classes, as detect/2 already did. They no longer see the mount's other
    configuration.
  • Breaking: The source identifiers a custom source adapter receives are
    ImagePipe.Source.Path, ImagePipe.Source.URL, and ImagePipe.Source.Object.
    Replace ImagePipe.Plan.Source.Path, .URL, and .Object in your adapter.
  • Breaking: A source's internal_cache takes :enabled or :disabled,
    and defaults to :enabled. Replace internal_cache: :auto, which meant the
    same as :enabled.
  • Breaking: c:ImagePipe.Source.S3.CredentialProvider.fetch_credentials/2
    takes the bucket and the provider's options. Change your provider's
    fetch_credentials/3 to fetch_credentials/2 by dropping the third
    argument, which was always [].
  • Breaking: cache and input_cache take the file system cache's options
    directly. Replace cache: {ImagePipe.Cache.FileSystem, root: "..."} with
    cache: [root: "..."], and the same for input_cache. The server's
    [cache] configuration is unchanged.
  • Breaking: The tracer now creates OpenTelemetry spans as they happen, and
    needs the OpenTelemetry SDK in your application. Remove the exporter:
    option from ImagePipe.Telemetry.attach_tracer/1 and the
    ImagePipe.Telemetry.Trace.OtelIdGenerator id_generator setting from your
    :opentelemetry configuration. To print spans without a backend, use the
    SDK's stdout exporter. Span and trace IDs now come from the SDK, and each
    span reaches the SDK when it ends, not when the request finishes.
  • A request's spans join the trace of the span that is current when the
    request starts, such as one from your Phoenix or Bandit instrumentation.
    This includes ImagePipe.run/4 in an instrumented job. With
    extract_inbound: true, the inbound traceparent applies only when no span
    is current.
  • Spans your own code opens inside ImagePipe's processes, such as in a custom
    source or detector, nest under the request's spans, with or without the
    tracer attached.
  • A request that continues an inbound trace follows the caller's sampled
    flag under the SDK's default parent-based sampler. It was always recorded
    before.
  • Breaking: [:cache, :eviction, :stop] reports the entries a write
    evicts, with trigger: :admission, and the startup scan's batches with
    trigger: :startup. Update handlers that match trigger: :reconcile.
  • Responses no longer wait for the cache's disk. A processed image is stored
    in the background after it's sent, and a downloaded original while the
    request processes it. The [:cache, :write] span can end after the
    request.
  • A request waiting for another request's image or original stops waiting
    when the image or original isn't stored 2 seconds after it was sent or
    downloaded.
  • When the disk can't keep up, writes are dropped, and unfinished writes
    count against max_size_bytes. An entry larger than a twentieth of
    max_size_bytes, or 256 MiB, is never stored (see
    writing entries).
  • trim is faster on images under a megapixel: 2 ms instead of 6.5 ms on a
    256×256 image.
  • Breaking: cache and input_cache need max_size_bytes, and an
    ImagePipe instance (see ImagePipe.child_spec/1). Add max_size_bytes to
    each cache, and to the options of ImagePipe.Cache.FileSystem.get/2, and
    move a configuration with a cache from ImagePipe.Plug options or an
    ImagePipe.run/4 call into an instance. run/4 with an instance name
    can't set cache or input_cache per call.
  • Breaking: Each root of cache and input_cache serves one node. An
    instance writes a .image-pipe-owner file into each cache's root.
    While another node keeps that file updated, the instance logs an error and
    runs without that cache, and it takes the root over once the other node
    stops. Give each replica its own cache directories, such as its own volume.
    When a node is killed, a node on another host that starts after the kill
    takes over its root about 30 seconds after starting.
  • cache and input_cache keep the entries that are requested often and
    took the most work to make for their size. For a processed image, that work
    is estimated from the request instead of timed (see
    work to make an entry).
    On startup a cache reads only file names, so a large cache is ready sooner.

Removed

  • Breaking: ImagePipe.Transform.Detector.Composite.new/1 and
    default/0. A detector is configured as a module, so a composite struct
    could not be configured. Configure ImagePipe.Transform.Detector.Composite
    itself to get the default face and object detectors.
  • Breaking: Bounded caches keep request counts in memory only. Remove
    node_id, state_dir, flush_interval, cleanup_interval, and
    state_ttl from cache and input_cache. After a restart, a bounded
    cache keeps its entries and learns again which are requested most. You can
    delete the .cache_state directory under each bounded cache's root.
  • The [:cache, :warm_start], [:cache, :flush, :stop], and
    [:cache, :cleanup, :stop] telemetry events.
  • Breaking: The eviction_victim_limit and `reconcile_i...
Read more

image_pipe_svg-v0.1.0

Choose a tag to compare

@github-actions github-actions released this 13 Oct 14:40
4066fa5

Added

  • Rendering SVG for ImagePipe with resvg, through a precompiled native
    library. Rendering never reads files or opens connections, draws text only
    in the fonts it's given, and accepts embedded PNG, JPEG, GIF, and WebP
    images and SVGs nested two levels deep. It bounds how far entities and
    <use> can expand, and how much text it lays out, before parsing, and
    estimates the cost of a render before it renders, so ImagePipe can reject
    an SVG that's too expensive.

image_pipe_server 0.2.0

Choose a tag to compare

@github-actions github-actions released this 13 Oct 15:02
4107245

Added

  • The IPS_CONFIG_TOML environment variable holds the whole TOML
    configuration, for platforms that can't mount a configuration file. IPS_
    setting variables override it as they override the file. Setting both
    IPS_CONFIG and IPS_CONFIG_TOML is an error.
  • SVG originals and SVG watermarks render like other images. An SVG renders
    straight at the size the request needs, so it stays sharp at any size, and
    a resize can make it larger than its own size without enlarge. Without a
    format or a negotiated one, the result is a PNG.
  • [processing.svg] configures SVG rendering: font_dirs and
    fallback_family for the fonts SVG text renders in (mount a font
    directory into the container, since no system fonts are used), and
    max_bytes, max_render_work, and max_render_pixels to limit the work
    an SVG can cause. A font_dirs entry that doesn't exist, or a file in it
    that isn't a font, stops the server from starting.
  • Logs and traces have an image_pipe.source.svg_render span for each SVG
    rendered, with its own and rendered sizes, the estimated work, and the
    limit that rejected an SVG.
  • Caches delete files they can't read, such as ones another ImagePipe
    version wrote, once the files are more than an hour old. The
    image_pipe.cache.sweep span reports them as unreadable.
  • Cache lookup and write spans, and cache stage span events, carry a
    cache_key attribute, the hash of the entry's cache key. Lookup spans also
    carry entry (response or source_record), and stage events carry
    pool (output or input).
  • Cache write log lines and spans carry bytes, the size of the stored
    image, and lookups carry it on a hit for a processed image. Write spans
    also carry estimated_cost_us, the work the cache estimates it took to
    make the image.
  • The image_pipe.request span carries a request_id attribute, the ID
    that tags the request's log lines.
  • Traces have an image_pipe.source.decode_open span for each time the
    original is opened for decoding.

Changed

  • Breaking: [cache.output] and [cache.input] need max_size_bytes.
    A cache without it stops the server from starting with
    cache.output.max_size_bytes: required. Set max_size_bytes on each
    cache, below the free space on its volume.
  • Breaking: Each cache root serves one server. The server writes a
    .image-pipe-owner file into each cache root. While another server keeps
    that file updated, the server logs an error and runs without that cache,
    and it takes the root over once the other server stops. Give each replica
    its own cache volume. A server takes over a root left by a crashed server
    on another host about 30 seconds after it starts.
  • Breaking: A source's internal_cache takes "enabled" or
    "disabled", and defaults to "enabled". Replace
    internal_cache = "auto", which meant the same as "enabled".
  • Breaking: An allowed_hosts entry of an http source allows only the
    scheme's default port, 80 or 443, including for redirect targets. Add an
    entry with the port, such as "assets.example.com:8443", for an origin on
    another port. A base_url with a non-default port needs that port in
    allowed_hosts when the list is set. A malformed entry, such as
    "assets.example.com:https", stops the server from starting.
  • Breaking: The image_pipe.cache.eviction trace event and the cache
    eviction log line report trigger as admission or startup instead of reconcile.
  • Processed images and originals cached by an earlier version miss once,
    because cache keys and file names changed. The cache refills as requests
    come in and deletes the old files. Cache keys now also include the SVG
    renderer and the fonts, so changing the fonts or the fallback family makes
    cached images miss again.
  • The cache keeps the images that are requested often and took the most
    work to make for their size. At startup it reads only file names, so a
    large cache is ready sooner.
  • Responses no longer wait for the cache volume. A processed image is stored
    in the background after it's sent, and a downloaded original while the
    request processes it. A request waiting for another request's image or
    original stops waiting when it isn't stored 2 seconds after it was sent or
    downloaded, and the image_pipe.cache.coordination trace event reports
    reason as deadline.
  • When the cache volume can't keep up, writes are dropped instead of queued,
    and unfinished writes count against max_size_bytes. A write evicts what
    it needs before it's stored, so stored images stay under
    max_size_bytes. An image larger than a twentieth of max_size_bytes, or
    256 MiB, is never stored. Each dropped write logs stage_dropped at
    warning.
  • A response to a URL with expires drops stale-while-revalidate and
    stale-if-error, because caches can't use them alongside its
    must-revalidate.
  • Each trace span is exported when it ends, not when the request finishes.
  • With trust_traceparent, a request that carries a traceparent header
    follows the caller's sampled flag. Before, it was always exported.
  • The server uses about 140 MiB less memory: it loads its code when it first
    needs it instead of all at startup. Set RELEASE_MODE=embedded to load
    everything at startup.
  • Before it reports ready, the server processes a small image in each output
    format, so the first requests don't wait for the image-processing code to
    load or an encoder to start. /health/ready now answers 503 until then, including on the
    separate health_port listener.
  • Images process faster, PNG most of all. The Docker images now ship zlib-ng
    2.3.3, libjpeg-turbo 3.2.0 and libpng 1.6.59 in place of Debian's zlib,
    libjpeg-turbo and libpng. Across a set of typical requests, latency dropped
    by about a quarter, and by more than half for some PNG resizes.
  • The server handles about 5–10% more images per second when the container
    has a CPU limit: the Erlang VM's schedulers no longer spin while they wait
    for work, which took CPU that image processing needed.
  • trim is faster on images under a megapixel.
  • PNG originals, and others that need no shrink-on-load, are opened once
    instead of twice.
  • Uses image_pipe
    0.2.0.

Removed

  • Breaking: node_id, state_dir, flush_interval,
    cleanup_interval, and state_ttl in [cache.output] and
    [cache.input]. The cache keeps request counts in memory, so after a
    restart it keeps its entries and learns again which are requested most.
    Remove the settings, and delete each cache's state_dir, by default
    .cache_state under its root.
  • Breaking: eviction_victim_limit and reconcile_interval in
    [cache.output] and [cache.input]. Remove them from your configuration.
  • Breaking: window_ratio, sketch_depth, sketch_width,
    aging_sample_size, doorkeeper_cardinality, doorkeeper_fpr, and
    path_prefix in [cache.output] and [cache.input]. Remove them from
    your configuration. The cache no longer reads or cleans up the entries
    under a path_prefix directory, so delete that directory.
  • Breaking: rescan_interval in [cache.output] and [cache.input],
    and the image_pipe.cache.rescan span and log line. Remove
    rescan_interval from your configuration. A cache deletes leftover files
    after its startup scan and then every 24 hours.
  • Breaking: The image_pipe.cache.warm_start span and the cache flush
    and cleanup span events. Remove alerts or queries that use them.
  • Cache stage events no longer report stage_cleanup_error.

Fixed

  • A cache write log line and span report an error when the cache isn't
    running, instead of reporting the image as written.
  • A comma inside a quoted Accept parameter, as in
    image/webp;x="a,b";q=0, no longer splits the entry and drops the q=0,
    so the server no longer chooses a format the client excluded.
  • With [http] allow_origin set, cross-origin scripts can send conditional
    requests such as If-None-Match and read headers such as ETag and
    Content-Disposition. Preflight requests get
    Access-Control-Allow-Headers: *, and responses get
    Access-Control-Expose-Headers: *.
  • With max-bytes, a quality search that ran out of encode attempts ships
    the highest encoded quality that fits instead of the lowest. When only the
    lowest quality fits, X-ImagePipe-AQ-Outcome and the logs report hit.
  • A crop placed with focus on an image with an EXIF orientation other
    than 1 or 5 could land one pixel off when its position fell on a half
    pixel.
  • The source cache stores and reuses originals whose Cache-Control has a
    qualified private="..." or no-cache="..." naming only headers
    ImagePipe doesn't keep, such as private="Set-Cookie". Before, they were
    never stored, or revalidated on every request.
  • Concurrent fetches from an http source just after startup could fail.
    They now wait for the connection pool to start.
  • A CMYK or other non-RGB source with an embedded color profile and more
    pixels than max_intermediate_pixels fails with 422, like other
    oversized images, instead of 415.

Built with image_pipe and image_pipe_url 0.2.0.

v0.1.0

Choose a tag to compare

@github-actions github-actions released this 09 Oct 11:58
8a0585b

image_pipe_url

Added

First release of the ImagePipe URL builder.

  • Build typed processing plans and generate image URLs with explicit processing
    groups and output options.
  • Validate processing options, reference named presets with request overrides,
    and optionally check plans against the serving mount's presets.
  • Sign URLs, set expiry, and encrypt source references.
  • Generate URLs in applications that use an external image service, without
    libvips, NIFs, or image processing dependencies.

image_pipe

Added

First release of ImagePipe, with image processing powered by
elixir-image/image,
Vix, and libvips.

  • Serve transformed images on demand from Phoenix or any Plug application
    through ImagePipe.Plug. Reuse the same processing plans and configuration
    for uploads, files, and background jobs through the Elixir API.
  • Read originals from local files, HTTP(S), S3-compatible storage, or custom
    source adapters.
  • Compose resizing, cropping, orientation, effects, and watermarks in explicit
    processing groups, with optional face and object detection, or your own
    detector.
  • Define reusable presets and request defaults in your configuration,
    statically or through a host preset lookup backed by a database or cache.
  • Encode JPEG, PNG, WebP, and AVIF, with format negotiation, quality controls,
    byte budgets, and color-profile policies. Automatic quality gives each image
    the lowest quality that still looks as good as a target you choose. Generate
    BlurHash, LQIP CSS, and image-info JSON.
  • Configure input and output caches, conditional HTTP responses, streamed
    delivery, request safety limits, and processing concurrency. Observe requests
    through telemetry, logging, and tracing.

image_pipe_server 0.1.0

Choose a tag to compare

@github-actions github-actions released this 09 Oct 12:05
8a0585b

Added

First release of the standalone ImagePipe server.

  • Run a dedicated HTTP image service from the published Docker images at
    ghcr.io/hlindset/image_pipe_server, or build it as a Mix release. Configure
    it through TOML and environment variables, without writing an Elixir
    application.
  • Configure file, HTTP(S), and S3 sources, filesystem caches, signing keys,
    presets, and processing limits. Load secrets from mounted files.
  • Serve images under a configurable mount path, with health checks, request
    IDs, optional bearer authentication, logging, and OpenTelemetry export.
  • Choose a base image or a vision image with bundled face and object detection
    models.

Built with image_pipe and image_pipe_url 0.1.0.