Best Strategy for Erratic Volatile Records

I am developing a system in which, I believe, there will be many users. Each user has a profile presented inside the application as a record. To save the user profile, I do the following base64:encode_to_string(term_to_binary(Profile)) , so basically the profiles are stored in a serialized maner.

So far so good. Now the question is:

From time to time, I plan to expand the profile’s functionality by adding and removing certain fields in it. My question is the best strategy for handling these changes in code?

The approach that I see at the moment is this:

 Profile = get_profile(UserName), case is_record(Profile, #profile1) of true -> % do stuff with Profile#profile1 ok; _ -> next end, case is_record(Profile, #profile2) of true -> % do stuff with Profile#profile2 ok; _ -> next end, 

I want to know if there are better solutions for my task?

Additional Information: I am using a simple KV repository. It cannot store Erlang types, so I use State#state.player#player.chips#chips.br

+6
source share
4 answers

Perhaps you could use propolitors.

Suppose you saved some user profile.

 User = [{name,"John"},{surname,"Dow"}]. store_profile(User). 

Then after a couple of years you decided to expand the user profile with the age of the user.

 User = [{name,"John"},{surname,"Dow"},{age,23}]. store_profile(User). 

Now you need to get the user profile from DB

 get_val(Key,Profile) -> V = lists:keyfind(Key,1,Profile), case V of {_,Val} -> Val; _ -> undefined end. User = get_profile(). UserName = get_val(name,User). UserAge = get_val(age,User). 

If you get the user profile "version 2", you will get the actual age (23 in this particular case).

If you get the user profile “version 1” (“old”), you will get “undefined” as the age, and then you can update the profile and save it with a new value, so this will be the object of the “new version”.

Thus, the version conflict does not change.

This may not be the best way to do it, but in some cases it may be a solution.

+1
source

This greatly depends on the fraction of the number of records, the frequency of changes and the allowable shutdown. I would prefer to upgrade profiles to the latest version first. You can also make a system that will be updated on the fly, as mnesia does. And finally, it’s possible to save code for all versions that I would definitely not prefer. This is a nightmare to service.

In any case, when is_record/2 allowed in security devices, I would prefer

 case Profile of X when is_record(X, profile1) -> % do stuff with Profile#profile1 ok; X when is_record(X, profile2) -> % do stuff with Profile#profile2 ok end 

Note that the catch clause does not exist, because what would you do with an unknown record type? The mistake is so fast!

You have many other options, for example. hack like:

 case element(1,Profile) of profile1 -> % do stuff with Profile#profile1 ok; profile2 -> % do stuff with Profile#profile2 ok end 

or something like

 {_, F} = lists:keyfind({element(1,Profile), size(Profile)}, [{{profile1, record_info(size, profile1)}, fun foo:bar/1}, {{profile2, record_info(size, profile2)}, fun foo:baz/1}]), F(Profile). 

and many other features.

+1
source

The best approach is to have a copy of the serialized (profile), as well as a copy of the same, but in recording form. Then, every time changes are made to the profile of the recording form, changes are also made to the serialized profile of the same ATOMICALLY user (within the same transaction!). Code that changes the user record profile should always recount the new serialized form, which for you is the external representation of the user record

  -record (record_prof, {name, age, sex}).
 -record (myuser, {
             username,
             record_profile = #record_prof {},
             serialized_profile
         }). 
change_profile (Username, age, NewValue) -> %% transaction starts here .... [MyUser] = mnesia: read ({myuser, Username}), Rec = MyUser # myuser.record_profile, NewRec = Rec # record_prof {age = NewValue}, NewSerialised = serialise_profile (NewRec), NewUser = MyUser # myuser { record_profile = NewRec, serialized_profile = NewSerialised }, write_back (NewUser), %% transaction ends here ..... ok
So whatever the serialization function is what it is. But this always leaves overhead free profile changes. Therefore, we keep the serialized profile as always the correct representation of the record profile at all times. When changes occur with the recording profile, the serialized form must also be recalculated (transactional) in order to have integrity.
+1
source

You can use any extensible data serialization format, such as JSON or Google Protocol Buffers.

Both of these formats support the addition of new fields without breaking backward compatibility. Using them, you do not need to introduce explicit version control in your serialized data structures.

The choice between the two formats depends on your use case. For example, using protocol buffers is more reliable, while JSON is easier to start with.

0
source

Source: https://habr.com/ru/post/900669/


All Articles