Updating structs when updating servers

Say you have a server running and it’s keeping all its data in a struct. If I want to update the server one thing I will typically do is to modify its data, which means modify the struct. Now I want to do this while the system is running. OTP provides support for doing this but I still have to write the code which converts the old struct to the new struct. So worst case I will have a function which needs both old struct and new struct at the same time. What is the best practice for doing this?

One way is of course to work on the struct as a map and modify the keys directly. This works of course but feels a bit hacky. Another solution is to use a map directly but then you lose the benefits of a struct.

So how do people attack this problem?

Robert

4 Likes

Hi Robert,

That’s a fascinating question. I look forward to the discussion that follows. My first thought would be to look to Erlang for wisdom and best practices, but IIRC there is not a Struct equivalent in Erlang, just Maps. And if LFE had an answer, you’d be the one to know it. This seems to be a uniquely Elixir question among BEAM languages.

I haven’t enough experience to have actually done this, but my first thought would be to encapsulate the translation logic within a function in the struct’s module. I don’t see any way of not treating the old struct as a map. Some sort of a version or similar field in the struct might be helpful over time.

2 Likes

What you might be able to do, is to use the OTP code_change method, but not refer to your struct as a struct, but as a map that has the __struct__: field. (because this is underwater what a struct is)

So if I have:

defmodule Foo do
  defstruct bar: 1
end

and this is rewritten to

defmodule Foo do
  defstruct baz: 1
end

then I can still refer to my old structs as %{__struct__: Foo, bar: 1} (while the new version can be referred to as the normal struct %Foo{}).

2 Likes

@Qqwy that is pretty much the hacky solution I mentioned. :slight_smile: It works but it doesn’t feel right.

In Erlang you generally use records and you have exactly the same problem and the same solution. In code_change you have to know explicitly what the tuple for the old record looked like. One work-around I have seen is that you don’t use the record directly but through access functions in a separate “record” module. These functions then know about all the different versions and can do the right thing. Just a more thorough hack which is quite safe and portable.

And LFE doesn’t have any better solutions, not yet anyway. Back to thinking about it.

Robert

5 Likes