Storing options in memory

Perhaps memory is not the right term, but is storing a list of atoms outside the database a viable or recommended pattern?

An example would be if I’ve a table of items that have a status attached to each of them (for example: pending, active and expired). Creating a schema and table for this list of status options seems excessive, as whilst they might be added to further down the line, it will never be in a scenario where one install must be different from another (i.e. the database can handle

Having them hard-coded somewhere in my app feels like the right choice, but how would I go about this? Knowing nothing about ETS tables, would loading these options into an ETS table on application startup be the correct method?

Moreover is this a viable/recommended pattern for such types of data?

Even though it usually is against anything what I have learned about normalisation of databases, but why not just store it as a string in the table? Even a human can then understand what the result of the query means, without having to look up numbers either in another table or even worse, your source code…

That was my first instinct, but just felt that because it’s a list of strings/atoms that is never/rarely going to change that a DB table might be overkill?

I thought you have a table of items? The items have a column with their status, just make that status plaintext, "pending", "active", etc.

It will be hard to refactor later on if you realize that you need 20 statuses more though…

That’s it. I didn’t want to just allow string because of the potential problems refactoring, and also wanted to constrain the options to a select few, which is why the reference to another table/list felt like a better option.

If you want to restrict and ensure referential integrety of the data, perhaps you can use enum types?

https://www.postgresql.org/docs/9.6/static/functions-enum.html

As far as I can remember, MySQL does have something similar. I’m not sure though, how good it is supported in ecto.

OK, I’ll take a look at those and see if those will fit the bill.

If it ‘truly’ is rarely changed but needs to be accessed often then just bake it into a module at compile-time, you can recompile it if needing to update it, the fastglobal library handles that for you too so as to make it a little easier.

But overall I’d probably put it in ETS or so in the general case.

Aha, I think that’s the approach I was after @OvermindDL1. Thanks for the link. I’ll test it out and see how it fairs.

Cheers all!

1 Like

For reference, so that others can see what I was on about, here’s what I’m running with at the moment:

defmodule Tic.Marketplace.ItemStatus do

  @statuses [
    "live",
    "pending_payment",
    "archived",
    "suspended",
    "deleted",
  ]

  def all_status() do
    @statuses
  end

  @doc """
    Checks if the supplied status is allowed
  """
  @spec check_valid_status(String.t) :: boolean
  def check_valid_status(given_status) do
    Enum.member?(@statuses, given_status)
  end

end

Which I use in my Item as a simple :string in the schema. As part of the Item changeset, I’ve got a function that checks for a valid status, which looks like this:

def validate_status(changeset) do
    valid? =
      changeset.data.status
      |> Tic.Marketplace.ItemStatus.check_valid_status

    if valid? do
      changeset
    else
      Ecto.Changeset.add_error(changeset, :status, "invalid status")
    end
  end

It’s probably not the perfect, or even recommended approach, but I wanted to work out how to accomplish without using a DB to enforce constraints. It was as much an exercise as a solution to a problem.

Cheers for all the help guys.

If they will change rarely or never, then I’d write a function that returns the list and hard-code the list. In the future if you find they change more often then change the function implementation.

I think you can also use validate_inclusion/4 if you’re going to do it that way

That’s a really nice function that I haven’t seen before. Fits the bill perfectly!