Цитата:
|
Цитата:
if (!ind>=0) лишний "!" в итоге условие всегда истинное, даже когда ind === -1. В этих случаях splice мгновенно, за O(1), удаляет последний элемент. Массив стремительно уменьшается, последующие итерации отрабатывают быстрее. Если поправить баг, то всё норм. |
Цитата:
|
Цитата:
Исправил. Цитата:
Когда работа - найти, если нашли удалить, если не нашли добавить - то самобалансировка будет происходить очень часто. А она тоже требует времени. А если сравнивать время на операцию сравнения, и время на балансировку, то не понятно сильно ли большим будет выигрыш. |
voraa,
балансировка - накладные расходы, это да. Но она занимает по времени не более O(ln(n)), т.е. на больших объемах это всё равно копейки по сравнению с возможным растягиванием дерева и временем O(n). В данном топике, разумеется, лучше юзать хэшмапы для поиска по id. Но если понадобятся запросы вида "наибольшее значение, не превосходящее заданного" и т.д., при условии частых изменений набора, то мимо сбалансированных деревьев не пройти. |
Цитата:
и как извлекается первый элемента? как устроена очередь? если это просто массив с операцией shift, то может работать долго на длинной очереди. |
Логичнее было бы любой приходящий запрос помещать в очередь.
А когда какой то запрос реализуется и элементы из buffer удалятся, проверять всю очередь в поисках запросов, которые могут быть исполнены в данный момент. Очередь в виде списка можно сделать. |
Цитата:
а вот кейс проверки всей очереди выглядит как узкое место, если очередь длинная. Здесь и надо как-то оптимизировать. Самое простое - сделать хэшмэп с очередями по каждому участнику. Освободилась пара [Вася, Петя], берем очереди по Васе и Пете и просматриваем их параллельно (в элементах очереди должен быть ещё таймстамп, когда добавили). В итоге мы найдем либо задачу снова [Вася, Петя], либо задачи [Вася, Х] и [Петя, Y], где X и Y отсутствуют в хэшмепе занятых. Вот эти задачи можно запускать в обработку, не забыв удалить элементы из персональных очередей для Вася, Петя, X и Y. Худший случай - когда в этих очередях будет дохрена пар [Вася, А] и [Петя, Б], где А и Б - занятые. Что с этим делать, как их быстро поскипать, пока не совсем понятно. про очередь в виде списка согласен. Можно добавить ещё пул элементов, чтобы их переиспользовать, не создавать каждый раз новые объекты. |
Цитата:
Допустим косяком идут запросы со свободными, и вдруг появляются в очереди запросы, которые могут быть исполнены Например освободились А и Б. Но в очереди есть А-Б и приходит запрос Б-А. Усложняется алгоритм выбора, какой запрос исполнять. А поместив пришедший в очередь, и если просматривать всю очередь, а не только начальный, мы все равно выберем то, что может быть исполнено. |
Цитата:
Нам не ведомо, что быстрее на js создание нового объекта (это какая то внутренняя достаточно быстрая операция) или помещение и извлечение объекта из списка. (это уже программа, с интерпретацией байткода, вызовы функций, что тоже не самая дешевая операция) |
Цитата:
|
Цитата:
Меня тут больше смущает другой кейс: обрабатывается А-Б, в очереди сидит А-С, пришёл запрос С-Д. Вроде как он свободен и можно запускать, но тогда для участника С он выполнится до предыдущего. Если так нельзя, то надо проверять участников не только в хешмэпе текущих обрабатываемых, но ещё в хешмэпе персональных очередей, о котором я говорил выше. И если там нашлось, то ставить в очередь. |
Цитата:
|
Ну если там ТАК много запросов, тут тут уж пора кластеризацию обсуждать, микросервисы там, Redis, Kafka, связность, отказоустойчивость... :D
А на деле у автора вопроса там по 3,5 запроса в час.) |
Цитата:
|
Цитата:
Это тоже самое, что использовать WordPress или Bitrix для одностраничного сайта с одним номером телефона. Мало того, что они сами по себе тормозы лютые, так и ещё самое главное — зачем? Готовые решения здесь совершенно неуместны, потому что они все избыточны, создают дополнительные зависимости, требуют бОльших мощностей и значительно отбирают скорость. Исходя из логики программы — большинство запросов должны быть уникальными и выполняться сразу без использования очередей. Но мы должны быть готовы и к внеплановым ситуациям. Лучшим решением всегда будет то, что создано специально под конкретную задачу с нуля. |
Цитата:
|
Цитата:
Цитата:
А сначала должен что то передать С, что бы потом С мог передать это Д Если сначала выполнять С-Д, то у С может этого не быть. Задачу на корректнее ставить. |
Цитата:
Если в момент операции выясняется, что у С нет того, что надо передать Д, то будет возвращен ответ "Операция не выполнена — не достаточно того-то". И какой смысл С что-то передавать Д, если у него этого нет? Только если для DDOS-атаки. |
Цитата:
|
Странно это для клиентов будет.
Я посылаю запрос А передает С 10 бочек апельсинов. Потом посылаю запрос С передает Д 5 бочек апельсинов. Мне приходит ответ, что у С ничего нет. И я долго чешу репу, куда же они делись. Ведь он должен был от А получить 10. |
Корректнее выполнять операции с участием С, когда остальные операции с его участием уже выполнились.
|
Цитата:
Т.к. в buffer (список обрабатываемых участников) хранятся только имена участников, то Set отлично подходит для этой задачи — как в реализации, так и в скорости. В pool (очередь участников, которые не смогли попасть в buffer) у нас уже хранятся сами операции, состоящие из 2-х имен участников и действии, которые первый участник хочет сделать для второго. Т.к. Set может хранить только уникальные значения без ключей, то для pool подходит либо Map, либо Object. |
Цитата:
Если вы знаете, что вам участник А должен передать 10 бочек апельсинов, то очевидно же что вы не будете пытаться передать участнику Д 5 бочек, пока не убедитесь, что на вашем складе больше или равно 5 бочек. |
Цитата:
И как последовательно (или еще как) просматривать Map, не превращая его в массив? Как можно просматривать этот пул? Взять одну операцию, посмотреть, может ли она быть выполнена, взять следующую и т.д.? |
Цитата:
|
Цитата:
|
Цитата:
1. Если участников операции нет в buffer - добавляем в него и выполняем. 2. Если хотя бы один из участников есть в buffer, то делаем setTimeout() и пробуем до тех пор, пока не получится как в п.1 Причем это можно сделать как на стороне сервера, так и на стороне клиента. |
Цитата:
А пул - простой список. Пришедшие операции добавляются в конец. Есть два события: 1 - операция добавилась 2 - какая то операция выполнилась. По каждому из этих событий мы просматриваем очередь от начала до конца. Если какая то операция может быть выполнена, то она отправляется на выполнение. Опять же все завит от условий. Если в пуле десятки записей, то и массив с операциями push и splice будет прекрасно работать. Это внутренние хорошо оптимизированные операции. |
Цитата:
В 99% случаев для каждого участника операции будет свободное место в buffer. А если и не будет - то оно очень скоро освободится. |
Весь вопрос на сколько таймер ставить? мало поставить - создастся очередь событий из сработавших таймеров. Много поставить, все это будет ждать лишнее время, и зачем тогда возились с оптимизацией?
Опять же где то надо хранить запросы, которые пришли, но не могут быть выполнены в момент прихода |
Если вы писали, что буфер на десятки тысяч (т.е это столько запросов, которые в данный момент выполняются / 2) , и даже больше, а в 99% случаев там нет участников очередного запроса, то на сколько же участников рассчитана эта система?
|
Цитата:
Опять же, вы забываете учитывать факт того, что это во всех случаях исключение, а не стандартный процесс. Если бы участникам приходилось каждый раз ожидать, то здесь очевидно было бы реализация очереди. Но в 99,9999% случаев им ждать совершенно не придётся. У меня сейчас такое впечатление складывается, что с внедрением очереди можно только навредить. Нет очереди — нет проблем. |
Цитата:
А про реальную нагрузку было сказано, что это должно обрабатывать в районе не менее 1500 участников в секунду. |
Цитата:
Во первых лишняя нагрузка на сеть, туда сюда гонять запросы с ответами, что выполнить не может. Нагрузка на сервер - получать лишние запросы, которые уже были. Возможна такая ситуация, что пользователь А хочет что то передать С, но идет много других запросов с С, которые по несчастливой случайности обрабатываются первыми, пока А сидит с timeout-ом. И он все сидит, сидит, запросы все идут и отклоняются. Чем плоха очередь на сервере, я так и не понял. Но вам виднее, я всей задачи не знаю. |
Цитата:
Получается надо общую очередь всех запросов изначально хранить? Такая логика?: 1. На сервер поступают запросы и помещаются в pool 2. Из pool операции добавляются в buffer и удаляются. Если в buffer не может добавиться, то перемещается в конец pool Так? |
Не совсем.
pool - это не совсем очередь. Добавляется только в конец. А выбирается при просмотре от начала до конца все операции, которые могут быть выполнены. Просмотр пула делается так Берем операцию. Проверяем, может ли она быть выполнена (участников нет в буфере). Если может, то отправляем операцию на выполнение. При этом ее участники добавляются в буфер. Берем след. операцию из пула и т.д. Т.е добавляем мы только в конец, а взять можем откуда угодно, просматривая пул. При этом взятая операция, конечно, удаляется из пула. Просмотр пула инициируется при двух событиях: - Новая операция добавлена в конец пула, - Выполнилась какая то ранее отправленная на выполнение операция. При этом ее участники удаляются из буфера. Такой алгоритм я предлагаю. |
Цитата:
|
Цитата:
|
Цитата:
Придется в цикле ключи перебирать. И как взять первый? Перебрать все и найти минимальный? |
Часовой пояс GMT +3, время: 11:45. |