Метка времени на основе курсора - PullRequest
0 голосов
/ 18 ноября 2018

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

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

То есть, два пользователя могут сделать загрузку в одно и то же время, что, по сути, нарушает логику пагинации на основе курсора.
Например, дайте мне следующие 10 элементов из определенной временной отметки, которая была отправлена ​​в качестве курсора для получения следующих результатов. Когда эта временная метка содержит два сообщения, 1 сообщение может быть отброшено и пропущено, если оно не вписывается в предыдущий запрошенный диапазон подсчета (например, 10 сообщений, дублирующее сообщение которых будет находиться в местоположении 11).

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

Любое понимание высоко ценится!

Ответы [ 2 ]

0 голосов
/ 20 ноября 2018

Я сомневаюсь, что Twitter использует для этого идентификатор строки автоинкремента RDBMS.Существуют такие сервисы, как ZooKeeper, внешние по отношению к базе данных, с помощью которых можно реализовать глобальный идентификатор последовательности.Тем не менее, вы, возможно, не захотите иметь глобальный идентификатор последовательности, потому что если всем нужно запросить последовательность из того же источника, вы заставляете все сериализоваться, отказываясь от всей концепции распределенной обработки.

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

  • Получить определенное количество элементов, начиная с указанной метки времени, даже если естьнет такого ключа карты, используя get_by_key_rel_index_range().
  • Получить все элементы в интервале между двумя временными метками, используя get_by_key_interval().

Для моделирования твитов пользователя их идентификаторы могут храниться в такой карте, причем ключом записи является идентификатор пользователя.

Чтобы смоделировать временную шкалу пользователя, вы могли бы иметь записи временной шкалы пользователя (вводимые по идентификатору пользователя) с упорядоченным списком , содержащим [timestamp, tweet ID, .., ..] в качестве элементов.Это позволило бы элементам с одинаковой временной меткой существовать бок о бок (где на карте не может быть двух элементов с одинаковым ключом).

В этом случае используются следующие полезные операции со списком:

  • Получить определенное количество элементов, начиная с элемента, ближайшего к указанной метке времени, с помощью get_by_value_rel_rank_range()
  • Получить все элементы в интервале между двумя метками времени, используя get_by_value_interavl.

См. Упорядочение и сравнение элементов .

У меня есть примеры того, как операции List и Map можно использовать для моделированияздесь разные вещи: rbotzer / aerospike-cdt-examples .

0 голосов
/ 19 ноября 2018

Используйте ограниченные списки или ограниченные карты в качестве хранилища данных.

Фрагмент кода с закрытой картой или его вариант - сохраняет последние 10 обновлений:

public class CappedMap {
    public static int insert(AerospikeClient client, int i) {
        Key key = new Key("test", "testMap", "user1");
            MapPolicy mPolicy = new MapPolicy();    
                int retVal=0;
                try {
                client.operate(null, key, 
                        MapOperation.removeByIndexRange("myMap",-10,10,MapReturnType.INVERTED), 
                        // INVERTED introduced in server version 3.16.0.1
                        MapOperation.put(mPolicy, "myMap", Value.get(i), 
                        Value.get("A quick brown fox jumps right over a lazy dog") ));
                 } 
                 catch (AerospikeException e) {
                   System.out.println("Error Code: "+e.getResultCode());

             }  
                 return i;
    }
    public static void main(String[] args) {
        AerospikeClient client = new AerospikeClient("127.0.0.1", 3000);
                int retVal = 0;
        for (int i = 0; i < 123; i++) {
                  System.out.println("Inserting k = "+i);
                  i = insert(client, i);
        }
        client.close();
    }
}  
...