Каков идиоматический способ ограничения количества записей под определенным ключом в мнезии?

Я использую mnesia для хранения данных для пользователей, и запись представляет собой пакет, структурированный как

{ username, field1, filed2, timestamp }

Чтобы не допустить взрыва базы данных, я хочу установить ограничение на количество записей, принадлежащих определенному пользователю, скажем, если количество записей для пользователя достигает 500, то запись с самой старой меткой времени удаляется перед вставляется новая запись.

Есть ли эффективный способ сделать это?

Заранее спасибо.


person Chi Zhang    schedule 10.11.2011    source источник


Ответы (2)


Другой способ может заключаться в том, чтобы ваша запись была

{username, value_list, timestamp} 

где value_list содержит список значений. Ваш стол теперь может быть набором вместо сумки, и вы можете делать такие вещи

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

всякий раз, когда вы вставляете. NewList будет содержать не более 500 элементов, и, поскольку самые старые элементы находятся в конце, если у вас есть 501 элемент, последний будет в списке _ignore, который вы... игнорируете.

Вы торгуете постоянным поиском по вашему ключу для некоторых манипуляций со списком, но это может быть хорошим компромиссом в зависимости от вашего приложения. Вы также можете избавиться от поля метки времени и связанного с ним кода для поддержки этого поля.

person Jr0    schedule 10.12.2011
comment
Этот подход очень прост (на самом деле у меня есть что-то подобное в продакшене), но он может быть крайне неэффективным, если лимит на количество записей высок, потому что мне придется читать или записывать все данные пользователя, для каждой выполненной операции на этом пользователе. - person igorrs; 08.01.2013

Я представил две возможности. Тот, который соответствует вашему дизайну, и тот, который вносит небольшое изменение в определение вашей записи. Посмотрите, какой из них лучше всего соответствует вашим потребностям. Первый ниже работает в рамках вашего дизайна.

-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 within the transaction is not optimal. Lets think of a better way. Also, i do not know how your timestamps look like so am sure you have a way of sorting them and finding out the latest timestamp, pick out the record which owns this timestamp and then return it. I have given this solution just to fit your design. I also think the some where we need some indexing. The next implementation seems better to me. Some how if we can change the record definition and we introduce a field oldestwhich takes a bool() and we index it like this

-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 better to me. success !!

person Muzaaya Joshua    schedule 10.11.2011