frerich

frerich

Pg_large_objects - Dealing with PostgreSQL Large Objects

An application wishing to store larger amounts of data typically has two options for doing so:

  1. A new column on some table can be introduced; Postgres features a bytea type for this purpose. This is easy to implement but suffers from requiring to hold the complete data in memory when reading or writing, something which may not be viable beyond a few dozen megabytes. Efficient streaming or random-access operations are not practical.
  2. A separate cloud storage (e.g. AWS S3) could be used. This permits streaming but requires complicating the tech stack by depending on a new service. Bridging the two systems (e.g. ‘Delete all uploads for a given user ID’) requires Elixir support.

PostgreSQL features a ‘large objects’ facility which enables efficient streaming access to large (up to 4TB) files. This solves these problems

  • Unlike values in table columns, large objects can be streamed into/out of the database and permit random access operations.
  • Unlike e.g. S3, no new technology is needed. Large objects live side-by-side with the tables referencing them, operations like ‘Delete all uploads for a given user ID’ are just one SELECT statement.

The pg_large_objects | Hex package makes it easy to work with large objects.

The high-level API permits importing or exporting data to/from the database in a convenient and memory-efficient manner, e.g.

# Stream data into large object
{:ok, object_id} =
  "/tmp/recording.mov"
  |> File.stream!()
  |> Repo.import_large_object()

A lower-level API makes it easy to work with parts of large objects by exposing common random-access operations, e.g.

alias PgLargeObjects.LargeObject

{:ok, object} = LargeObject.open(object_id)
PLargeObject.seek(object, 1024)
{:ok, sixteen_bytes} = LargeObject.read(object, 16)

See the documentation at PgLargeObjects — PgLargeObjects v0.2.3 for more.

Most Liked

frerich

frerich

I just released version: PgLargeObjects — PgLargeObjects v0.2.0

This feature features a couple of documentation fixes, but most notably a ready-made implementation of the Phoenix.LiveView.UploadWriter behaviour: PgLargeObjects.UploadWriter. This makes it easy to consume file uploads in LiveView applications by streaming them straight to the database, using constant memory!

First, use the :writer option to Phoenix.LiveView.allow_upload/3 to specify the custom writer. Make sure to pass the :repo option in the tuple to indicate which repository the object should be stored in:

    socket
    |> allow_upload(:avatar,
      accept: :any,
      writer: fn _name, _entry, _socket ->
        {PgLargeObjects.UploadWriter, repo: MyApp.Repo}
      end
    )

Next, when consuming upload entries, extract the object ID referencing the upload and store it in order to be able to reference the data from elsewhere:

    consume_uploaded_entries(socket, :photo, fn meta, _entry ->
      %{object_id: object_id} = meta

      # Store `object_id` in database to retain handle to uploaded data.

      {:ok, nil}
    end)

Where Next?

Popular in Announcing Top

ityonemo
Currently just starting out on a new mini-project - getting zig NIFs to run in elixir. https://github.com/ityonemo/zigler The idea here...
New
BartOtten
Phoenix Live Favicon Favicon manipulation for Phoenix Live A lib enabling dynamic favicons in Phoenix Live View applications. To sho...
New
fuelen
Ecto.DevLogger is an alternative logger of SQL queries in development It inlines bindings into the query, so it is easy to copy-paste lo...
New
kasvith
Hello Everyone, I was working with some HTML-to-Markdown libraries and ran into a few issues when converting a complex markup file to Ma...
New
lud
Hello! I’ve been working on the Oaskit library for a while now, and just released a first version. Since I’ve built JSV I wanted to be ...
New
wingyplus
I just did a dirty hack after seeing Zoi on x.com a few hours ago. Quick Introduction The zoi_defstruct is a library to help you generat...
New
type1fool
WebAuthnLiveComponent WebAuthnComponents See this post about renaming the package. Passwordless authentication for Phoenix LiveView app...
New
mattmower
I’m pleased to announce that we’ve released DemoGen v0.1.7, a library for creating repeatable demo scenarios in your Ecto-based SaaS appl...
New
mudspot
Hi folks, I’d like to announce the release of Prometheus Agents, a set of subagents for Claude Code that cater to the Elixir Phoenix pro...
New
sodapopcan
I’ve heard that if you’re not embarrassed by v1 of your product Vim plugin then you’ve released too late. I’ve been sitting on this the ...
New

Other popular topics Top

_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New

We're in Beta

About us Mission Statement