the_wildgoose

the_wildgoose

Ecto Auto Migrator - (thoughts appreciated)

Hi, Wonder if any experts could glance over my tiny library here:
GitHub - nippynetworks/ecto_auto_migrator · GitHub

The substance is that sometimes there is a need to run migrations automatically at app startup and without an external mix migration step. Embedded would be one example, but I guess SASS products face the same.

The actual Ecto docs include a sample for creating a migration function, and elsewhere on this forum it recommends creating a simple genserver compatible module which can be added to the tree to run the migrations

All I have here is a simple skeleton around these two concepts as there was a bit of typing and a few constants to get correct. I will also need this in a couple of different projects.

Some extra lines I added were because I noticed that mix will of course start the app regularly, so will many build tools doing linting, etc, so I’ve wrapped the migrate function in a test which can be set in Config, which in turn I recommend it set via an env variable. This approximately gives the owner of the app the ability to run migrations in specific situations, eg deployed builds, but restrict it during development (where migrations might still be run manually)

My specific use case I need this migration step to pass no matter what (unattended embedded use case), so the migrate step is overrideable and in my case I replace it with some code that will blow away the DB (and recreate it) in the event of a migration failure (other options might be to restore a backup or ignore the migration, etc, depending on your situation)

Nothing terribly profound here, but it took me at least a couple of mins to get the architecture straight in my head, so hopefully this helps someone? Comments appreciated?

GitHub - nippynetworks/ecto_auto_migrator · GitHub

Most Liked

sbuttgereit

sbuttgereit

So I have a similarish desire in regard for migrations: startup the application and apply any outstanding migrations as needed in the startup routines. Naturally, there’s a bit more to it than that and I’m not in an embedded scenario, but there are reasons for this approach I believe are valid for wanting my application itself to govern the running of database updates rather than have that as a separate administrative task. So my solution: don’t use Ecto migrations for this purpose or try to bend it to my will.

My viewpoint as a still relative outsider to the world of Elixir is this: Ecto as a tool is designed to meet the needs of a general audience, but a certain kind of general audience. If you’re building a sort of typical web-app and your product is delivered via that web-app, where the database is mostly simpler persistence for a specific application, where you’re doing rigorous continuous integration & (probably) continuous deployment, you don’t have database specialists involved in development (so called “application DBAs”), and you have full control over both the development of the application and the operations of the application: my assessment of Ecto is that it’s most at home in this kind of environment. We can probably extend this a bit if we allow just the operational control by qualified staffing, where the application is brought in from elsewhere, like you might find in libraries or certain utility apps that you might add into your operation. (I’m probably speaking more about Ecto SQL throughout this post moreso than Ecto, but I’ll just keep saying Ecto for now, you know what I mean).

But leave the model that Ecto best supports and it quickly makes less sense to use and trying to make Ecto be all things to all people I think will ultimately diminish the value it does provide to its intended audience.

Watching the forums here and talk around the Web, you’d think that database access and Ecto are necessary partners, but they aren’t. Ecto is a very helpful library that will get you through a lot of use cases. But when it doesn’t hit the mark, it’s probably best to just leave Ecto and its opinions on the shelf and find something else (OK, there really isn’t a lot to find) or make something bespoke to deal with your specific needs… the complexity and size of Ecto is probably because the way it is because it’s trying to be a generalized solution for these kinds of applications.

I looked at the specific problems I’m trying to solve and decided to roll my own migrator (just finishing up that project). I don’t need many of the features that Ecto brings with it and Ecto doesn’t support a number of features I think are important to this specific application, and it’s not really that much code to make things work: it just needs to support my model for database updates well and handle the failure modes that can come out of that model in a sufficiently predicable manner. So in this regard, Postgrex is much more important to my application than Ecto SQL. But the requirements I’ve set for my application are niche compared to the common use cases for Elixir and I wouldn’t suggest anyone follow my model unless they have clear reasons to do something similar. Of course, there’s the query builders and the DSL around building out the schema with Ecto… but I don’t need that either. Plain old SQL is completely sufficient and I’m probably better served since the database does more in my application than in many web-apps.

Anyway… I guess I’m just trying to say is extending the target use cases in a well established something like Ecto isn’t necessarily bad, but it’s worth asking if you should rather than if you can and that’s basically the messaging that I’m seeing in this thread: many voices not sold on extending Ecto in this direction and I think they’re probably right.

Thanks,
Steve

P.S. Perhaps Ironically, I am using Ecto proper for it’s changesets and validations in dealing with internal validations, form processing, etc. I could gin up something for that, too, but for my use case it actually works well so far. I may end up going elsewhere in the end, but for right now those parts of Ecto are superior to anything I could build myself and will likely be more than good enough.

LostKobrakai

LostKobrakai

That’s exactly the complexity I was hinting at in my last post. I don’t think I have the one solution here given all approaches come with their individual tradeoffs. The more “dynamic” or unknown (as opposed to static) a system is the more tricky it becomes to handle its state. In the end you’ll need some place to know of all the subcomponents you have. When using the individual Application.start callbacks then this place would be the startup list of applications in your release. Generally I’d suggest having some central component in your system knowing about subcomponents, because then you are in control – instead of delegating to a piece of code you don’t directly control. With that you could have something like a MyCentralApp.run_migrations delegate to a known module in each subcomponents, which handles their individual migration needs. Something like

for sub <- fetch_subcomponent_roots() do
  Module.concat(sub, Migration).run_migration()
end

I’d strongly suggest to let each application deal with it’s own migration needs, ot

That’s imo completely unrelated to migrations, but to anything your dependencies do. I want to start that this is generally not something the beam has a simple solution to. If an application crashes (and it’s a permanent one) then the vm will shut down and is expected to come back online by means outside the vm. Also restarts are the means of recovery on the beam, but (especially without backoff) they can only fix a subset of possible errors in a system. The goal of tools provided by the beam and otp is to keep all the stuff running, which you tell it to keep running and it’ll give up if it cannot do so.

So to build a system, where partial availability is more important than complete availability you’ll need to build in the guard rails. Those could be circuit breakers, starting applications (see e.g. the shoehorn library) or processes with non :permanent restart types, using more elaborate restart mechanisms with backoffs, custom logic (see e.g. the parent library). You’ll essentially want to be able to shut off things, which continue to fail (causing partial downtime) in favor of keeping the ability to connect to the system from the outside. Doesn’t matter if a migration or other code is the cause of the errors.

This becomes even more tricky when you considere NIFs, which when they fail directly bring down the beam VM. Nothing application level can catch errors in NIFs. Also there are outside forces, which might stop your beam VM. E.g. when using linux the oom killer might stop the beam if you’re using to much memory.

While as you can see there are a lot of things to cover, this is not to say you cannot deal with those. But you’ll need to be diligent and careful, know your dependencies, know your failure cases, … The beam has many tools, which help build a system, which has the properties you need. You won’t be getting them automatically though.

stefanchrobot

stefanchrobot

I think this didn’t get much traction because that’s a rather unusual approach to migrations. Depending on the environment/build env, the usual thing to do is:

Local development (:dev):
manually run

mix ecto.migrate

Local testing/CI (:test):
run migrations as part of the tests - the test task is aliased to

test: ["ecto.create --quiet", "ecto.migrate", "test"]

in mix.exs

Production (:prod):

If you’re using Docker, this could be:

CMD ["sh", "-c", "bin/app eval MyApp.Release.migrate && bin/app start"]

Usually there are healthchecks in place, so if the migrations fail, the deployment process should pick up on that and abort the deployment.

The case in the original post was about running the new version regardless if the migrations succeed or not, but I think this can be easily achieved with running the migrations as a separate command and just ignoring any errors.

Where Next?

Popular in Announcing Top

danschultzer
In short Plug n’ play OAuth 2.0 provider library. Just set up a resource owner schema with Ecto (your user schema), install the dependen...
New
mathieuprog
Hello :waving_hand: Allow me to introduce you to Tz, an alternative time zone database support to Tzdata. Why another library? First a...
New
ostinelli
Let’s write a database! Well not really, but I think it’s a little sad that there doesn’t seem to be a simple in-memory distributed KV da...
New
restlessronin
The repo is at GitHub - cyberchitta/openai_ex: Community maintained Elixir library for OpenAI API · GitHub. Docs are at OpenaiEx User Gu...
152 10380 134
New
maltoe
Hello! Came here to announce ChromicPDF, a pet project PDF generator I’ve been working on for the past few months. Why another PDF gener...
New
bluzky
You may know https://ui.shadcn.com/, a UI component library for React. I really love it’s design style and components. I’ve built some co...
384 14206 119
New
kip
Image is an image processing library for Elixir. It is based upon the fabulous vix library that provides a libvips wrapper for Elixir. I...
622 19010 194
New
ahamez
Hi everyone, I’ve been working on this protobuf library for 3 years. We use it in the company I work for, EasyMile, to communicate with ...
New
cjen07
parameterized pipe in elixir: |n&gt; edit: negative index in |n&gt; and mixed usage with |&gt; are supported example: use ParamPipe ...
New
mplatts
With HEEX released we decided to start a components library using Tailwind CSS - check it out here: Petal Components. We also have a boi...
New

Other popular topics Top

electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 31194 112
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 54250 245
New

We're in Beta

About us Mission Statement