What is the idiomatic way to limit the number of entries under a particular key in mnesia?

I use mnesia to store data for users, and the record is a bag structured as

{username, field1, filed2, timestamp}

In order not to blow up the database, I want to set a limit on the number of records belonging to a particular user, say, if the number of records for a user reaches 500, then the record with the oldest timestamp is deleted before a new record is inserted.

Is there an effective way to do this?

Thanks in advance.

+4
source share
2 answers

I have provided two possibilities. One that matches your design, and one that introduces a slight change in your record definition. See which one best suits your needs. The first one works in your design.

  -record (user, {username, field1, field2, timestamp}).

 %% execute the function below in a mnesia transaction

 insert (#user {username = U, timestamp = _T} = User) ->
     case mnesia: read ({user, U}) of
         [] -> mnesia: write (User);
         AllHere -> 
             case length (AllHere) == 500 of
                 false -> %% not yet 500
                       mnesia: write (User);
                 true -> 
                     %% value has reached 500
                     %% get all timestamps and get the 
                     %% oldest record and delete it
                     %% 
                     OldRecord = get_oldest_stamp (AllHere),
                     ok = mnesia: delete_object (Record),
                     mnesia: write (User)
             end
     end.

 get_oldest_stamp (UserRecords) -> 
     %% here you do your sorting
     %% and return the record with
     %% oldest timestamp
     ....
     OldRecord.


The length/1 function inside a transaction is not optimal. Let's think about a better way. Also, I don’t know what your timestamps look like, so you’re sure that you have a way to sort them and determine the last timestamp, select the record that owns this timestamp, and then return it. I gave this solution to fit your design. I also think some where we need indexing . The following implementation seems to me better. Some, how can we change the definition of a record, and enter the oldest field, which accepts bool() , and we index it as follows:

  -record (user, {
             username,
             field1,
             field2,
             timestamp
             oldest %% bool (), indexed 
}).
insert_user (Username, Field1, Field2) -> User = #user { username = Username, field1 = Field1, field2 = Field2, timestamp = {date (), time ()}
}. insert (User).
%% execute this within a mnesia transaction
insert (#user {username = U} = User) -> case mnesia: read ({user, U}) of [] -> mnesia: write (User # user {oldest = true}); AllHere -> case length (AllHere) == 500 of false -> %% unset all existing records' oldest field %% to false F = fun (UserX) -> ok = mnesia: delete_object (UserX), ok = mnesia: write (UserX # user {oldest = false}) end [F (XX) || XX <- AllHere], ok = mnesia: write (User # user {oldest = true}); true -> [OldestRec] = mnesia: index_read (user, true, oldest), ok = mnesia: delete_object (OldestRec), ok = mnesia: write (User # user {oldest = true}) end end.
The above implementation seems to me better. success!
0
source

Another way might be to have your post

 {username, value_list, timestamp} 

where value_list contains a list of values. Now your table can be a set instead of a bag, and you can do such things

 {NewList,_ignore}=lists:Split(500, [{NewFld1,NewFld2}|Value_list]), %mnesia:write(Rec#{value_list=NewList}) type of code goes next 

whenever you insert. NewList will contain no more than 500 elements, and since the oldest elements are at the end, if you have 501 elements, the last will be in the _ignore list, which you will ignore.

Your trade by constantly searching for your key to process some lists, but it can be a good compromise depending on your application. You can also get rid of the timestamp field and its associated code to support this field.

0
source

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


All Articles