Skip to content

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 13 Oct 14:55
· 200 commits to main since this release
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_interval options
    of cache and input_cache. Remove them from your configuration. A write
    now evicts every entry it needs before it's stored, so the stored bodies
    stay under max_size_bytes once the startup scan has run, and the cache no
    longer runs a background eviction pass every minute.
  • Breaking: The window_ratio, sketch_depth, sketch_width,
    aging_sample_size, doorkeeper_cardinality, doorkeeper_fpr, and
    path_prefix options of cache and input_cache. Remove them from your
    configuration. The cache no longer reads or cleans up the old entries under
    a path_prefix directory, so delete that directory.
  • Breaking: The rescan_interval option of cache and input_cache, and
    the [:cache, :rescan] telemetry event. Remove rescan_interval from your
    configuration. A cache deletes leftover files after its startup scan
    and then every 24 hours.
  • [:cache, :stage] no longer reports cache: :stage_cleanup_error.
    Discarding a staged entry doesn't fail.
  • Breaking: ImagePipe.Telemetry.Trace.LogExporter,
    ImagePipe.Telemetry.Trace.OpenTelemetryExporter,
    ImagePipe.Telemetry.Trace.OtelIdGenerator,
    ImagePipe.Telemetry.Trace.Span, ImagePipe.Telemetry.Trace.Context, and
    the ImagePipe.Telemetry.Trace.Exporter behaviour.

Fixed

  • [:cache, :write] reports a write error instead of cache: :write when
    the cache's process isn't running, since nothing was stored.
  • A crop placed with focus on an image with an EXIF orientation other than
    1 or 5 could land one pixel off from the same crop on an untagged image,
    when the crop's position fell on a half pixel.
  • With max-bytes, a quality search that ran out of encode attempts
    shipped the lowest quality, even when it had already encoded a higher one
    that fit the budget. It now ships the highest encoded quality that fits. When
    only the lowest quality fits, the X-ImagePipe-AQ-Outcome debug header and
    the [:encode, :search] telemetry event report hit instead of
    best_effort.
  • With allow_origin set, cross-origin scripts can send conditional
    requests such as If-None-Match, and can read response headers such as
    ETag and Content-Disposition. Preflight requests now get
    Access-Control-Allow-Headers: *, and responses get
    Access-Control-Expose-Headers: *.
  • A comma inside a quoted Accept parameter, as in
    image/webp;x="a,b";q=0, no longer splits the entry. The split dropped the
    q=0, so ImagePipe could choose a format the client had excluded.
  • input_cache rejects max_body_bytes instead of ignoring it, and
    configuration fails with an unknown-option error. Remove the option from
    input_cache.
  • Concurrent fetches from an HTTP source (ImagePipe.Source.HTTP) just after
    startup could exit with :noproc. 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.
  • 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". They were treated as
    unqualified private (never stored) or no-cache (revalidated on every
    request).