- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
// На самом деле это пока только псевдокод. Real code will be on C/C++
//Lock-free non blocking and anatomic operation only if IP was changed
// IN SHARED MEMORY
non_atomic_in_shmem bad_IP_flag_non_atomic_in_shmem[64][16]; // in real code it will be uint64_t array[16] and bits operations with it
some_ip_class IPs_non_atomic_in_shmem[64][16] //
std::atomic<uint64_t> version_holder_atomic_in_shmem[64][16]; // it is debatable "whether to do it the Atomic" but for reinsurance
// IN WRITER PROCESS
void worker_reassign_IP_by_num(new_IP, back_end_server_ID, num)
if bad_IP_flag_non_atomic_in_shmem[back_end_server_ID][num] // in real code this check will be not here but will have same sense
version_holder_atomic_in_shmem[back_end_server_ID][num] ++ //it is increment of atomic var version and version become NOT even
__sync_synchronize(); //TODO may be we can use something better here ?
IPs_non_atomic_in_shmem[back_end_server_ID][num] = new_IP //it copy assignment
__sync_synchronize(); //TODO may be we can use something better here ?
version_holder_atomic_in_shmem[back_end_server_ID][num] ++ //it is increment of atomic var version and version become even
__sync_synchronize(); //TODO may be we can use something better here ?
bad_IP_flag_non_atomic_in_shmem[back_end_server_ID][num] = 0
// IN READER PROCESS
some_ip_class get_valid_ip_by_num(back_end_server_ID,num){
//TODO version_holder is atomic for now, may we do it non atomic? I think YES. Are any arguments in opposites?
// Instead of atomic can we possible use volatile?
uint64_t version_before = version_holder_atomic_in_shmem[back_end_server_ID][num] //it is atomic copy assignment
//if (version_befor & 1) // is not even it will work too instead of checking flags
// but checking flags it is less expensive operation than checking version because the version var is atomic
if bad_IP_flag_non_atomic_in_shmem //
return 0;
some_ip_class ip = IPs_non_atomic_in_shmem[back_end_server_ID][num]
uint64_t version_after = version_holder_atomic_in_shmem[back_end_server_ID][num] // it is atomic copy assignment
if version_before != version_after // versions is not sames
// we may add extra check of version evenest but it will cost us 1 slow atomic operation and excessively
//OR (version_after & 1) //or versions is not even ip is not correct
return 0
return ip
}
some_ip_class get_valid_ip(back_end_server_ID)
while(time_not_expaired) {
for (n=0; n<16; n++){
some_ip_class ip = get_valid_ip_by_num(back_end_server_ID, n)
if ip
return ip
}
//"it will never happened" "if it happened and time expired do something but it is problems on the back end servers site"
//”it is similar situation with situation when back end server just down ”
}
return 0 // or some server_down_ip ;)
some_ip_class check_or_get_new_ip( curent_ip, back_end_server_ID, num)
if NOT bad_IP_flag_non_atomic_in_shmem
if current_ip == shmem_array_of_IP_non_atomic[back_end_server_ID][num] // maybe need copy but I think it's not necessary
return current_ip
Задача: есть reader(ы) Это процессы 20 - 30 -64, которые обращаются к web-серверу. У web-сервера может быть несколько ip (не больше 16). Переодически (1 раз в ~30 секунд) пара (1-2) IP может отвалиться и вместо них может появится пара 1-2 новых. reader обнаружив bad IP выставляет флаг bad_IP_flag. Writer (только 1 единственный процесс не reader) Переодически (1 раз в ~1 сек) проверяет флаг, увидев bad_IP_flag запускает DNS-lookup который длиться примерно (30mks - 1s). Обновляет массив ip Readers при попытке соединиться проверяют не стал ли IP плохим если стал пробуют другой из массива Но суть не в этом Суть в том как безопасно работать с shared memory? Где что не так в предложенной задумке?
>it is problems
Hello, me call Boris, ai talk inglish very best.
> versions is not even
> Instead of atomic can we possible use volatile?
> Переодически
> вместо них может появится
> DNS-lookup который длиться
у меня плохие новости.
Полно инфы в интернете. Можно и тупо шаред-мутексы для начала брать, lockfree-очереди замутить поверх буста.
Ну и если райтер делает какие-то редкие долгие блокирующие операции, почему бы не начать с простой очереди сообщений вроде zeromq.
Всё с точностью до наоборот.
Шаред мемори + локи - неиссякаемый источник багов и косяков и имеет право на жизнь либо в условиях требований супер-низкой латенси, либо для реализации нормальных примитивов.
У вас же 16 ip, которые можно хоть все в один RPC-вызов запихать и RPS смехотворный, да ещё и всё на одной машине.
Я лично стараюсь всегда использовать очереди и пулы, даже в рамках одного процесса, а мутексов и разделяемых ресурсов избегать как огня в любых нетривиальных случаях.
Сокеты, опять же, гораздо лучше скейлятся, если вдруг надо расположить воркеров на нескольких машинах.
About complexity
Puzzle for “socket-based” system
Proxy 1[P1] found IP1 is bad, sent notification message[NM] to worker-process[W]
W sent broadcast[BR] and start DNS lookup[DN] (or start DN and sent BR?)
P2 found IP1 is bad before receive BR sent NM to W
Before receive NM from P2, W already did one more DN (because a Pn reports a IPn is bad) and in this DN W see the IP1 is become a good
Questions:
What has to do W with this bad-good IP1 ?
What has to do W if all 2 or 3 or 4 or N IPs will be bad-good?
In contrast to the almost unbelievable situation hang-upend for 30s process, this situation is not only quite possible it might even be considered normal for system based on sockets.
I sure this task can be solved intellectually. Also I'm sure that this puzzle is not easier than the puzzle with the shared memory and I'm also sure this is not the last such a puzzle for the asynchronous, non blocking, socket-based solutions with queues, broadcasts, slow data streams and multiple copies of same data.
Крепим к каждому сообщению числовую версию таблицы IP, которую имеет ридер. При каждом апдейте таблицы обновляем версию таблицы на писателе, рассылаем версию вместе с новой таблицей. Так писатель сразу поймёт, какие сообщения от читателей относятся к устаревшим данным.
Тем более, если реализация с сокетами уже есть и работает, смысла долбиться в шареную память - нуль.
И что? В чём проблема-то? Пришёл запрос на устаревшую версию - до свидания, обновляйся, проверяй снова.
> при бродкасте все процессы а их например может быть 64 должны отложить свои задачи и начать обрабатывать запрос.
Они в один поток работают и ждут каждый запрос синхронно?
> А он им нифига вообще не нужен, они сидят и работают с сервером и IP который уже 2 года не менялся.
Зачем тогда бродкастить? Сломалось - иди узнавай свежую инфу у мастера, ни к чему канал забивать.
> здесь все тоже кинулись предлагать сырые идеи
На этих "сырых" идеях работает куча баз данных со сложной семантикой и репликацией, но, конечно, этот подход полон фатальных недостатков, упущенных бездарными пейсателями сих убогих творений.
Ну и у меня стойкое ощущение, что тут происходит попытка завелосипедить уютный сервис координации, вроде ZooKeeper.
В этом то и проблема Ты провисишь в открытом http/tcp XX-s пока узнаешь что ты висишь на больном IP а уже через 1-2 сек мог бы переползти на новый туда куда твой сервер уполз. Люди потратили кучу денег и мозгочасов чтобы сделать миграцию живой а ты им серпом по ... В общем Вы не хуже меня знаете недостатки такого подхода Поищите лучше проблемы/ошибки в моем алгоритме. Нет там в чистом виде ни мьютексов ни локов и блокировок быть не может. Все работает как часы Быстрее за в 1000 sockets и вопросов в 100 раз меньше Правда чтобы понять где могут проблемы действительно нужно подумать Сложность бывает разная В одном случае голова не может вникнуть в другом случае не может охватить размах Это как бег на короткую или длинную дистанцию Одному проще напрячься на 10 сек Другому лучше дольше но не так интенсивно. ))
Откуда инфа? Есть замеры?
Расскажи мне лучше, как ты будешь без лока атомарно обновлять версию вместе с содержимым. Или атомарно сеттить адреса IPv6.
А потом ещё скажи, как ты без мутекса с кондваром будешь предотвращать 100% СPU на писателе в ожидании бэд-флагов от читателей.
Ну и столь нужный бродкаст ты чем собрался заменять.
Есть приблизительная оценка опирающаяся на замеры Почитай я уже это выкладывал Ну и сам прикинь Это полезное качество приближенно считать
"Расскажи мне лучше, как ты будешь без лока атомарно обновлять версию вместе с содержимым. Или атомарно сеттить адреса IPv6.
А потом ещё скажи, как ты без мутекса с кондваром будешь предотвращать 100% СPU на писателе в ожидании бэд-флагов от читателей."
Вот на эту тему я бы и хотел поговорит Если не трудно попытайся понять мою идею Там все подробно изложено если чуть мозг напрячь то в общем-то не так сложно (короткая дистанция))))
"Ну и столь нужный бродкаст ты чем собрался заменять."
Так у меня он по определению не нужен у меня копия данных то одна И bad_flag один на каждый IP но он один на всех клиентоа Он shared )) Вот почему shared лучше))
Поскольку у меня IP меняется не часто (1 раз в 30 сек пара IP из 16 на сервер) я могу просыпаться раз в 1 сек На семом деле этот процесс делает еще много чего неспешного Это такой периодикал хэлпер (удивляюсь как ты сам то не подумал что можно просто sleep если скучно и нечем занятся)))
Это пиздец, ребята. Зовите санитаров.
Если ридер посчитал адрес валидным, а через микросекунду его пометили битым, ридер будет весь tсp таймаут ждать ответа. А с бродкастом (который придёт быстро) можно отклеится постфактум и попробовать другой адрес.
Ну либо повесить в каждый ридер по треду и поллить состояние памяти атомарными ридами (сыкономили процессорное время, ага). В общем, выигрыш не такой очевидный.
маздайка instead of ты питух, atomic can we possible it might заедушный , анскилябра, even be considered as advanced in next 1-2 years, Царь this situation анскильная кукарекалка in case ты питух, of using заедушный питушок sockets ~2000 wider than анскильное говно for shared memory and анскилед i'm also я тут Царь питушня sure thistask can be ко-ко-ко solved intellectually. also заедушный питушок i'm sure this last such маздайка a puzzle with the shared memory?
полно инфы в интернете. говноязык , анскилябра, можно и тупо , животное, слился ты животное, шаред-мутексы для начала всегда говно использовать очереди замутить говноязык днище поверх буста.
puzzle днище for the тебе питушку asynchronize вроде бы хватит release семантикой , анскилябра, слился ты животное с репликацией, но, конечно, говноязык говно этот подход полон фатальных недостатков, ты кукарекушка, лишь кукарекаешь
Now we have 64 servers that can change (we think) 1 IP every 30s. 1 Broadcast every ~ 0.5s.
With 256 servers, we will be distracted by broadcast every ~0.1s.
With 1024 (as in Alteon) for every 30ms.
The same situation if we will figure out the servers change not 1 IP every 30s but 4 or 16. In a future lets call it 64 Floating IPs (64FIPs), 256FIPs, 1024FIPs.
Lets imaging we are running FV on Xeon-server with 128 cores. And 1 socket transduction is ~50мкs.
All processors all processors spend together 128x50=6400 mks for every broadcast. 6.4ms to doing something that may be not useful at all.
One more uncomfortable situation
All 128 processes just before receiving broadcast decide reports about bad IP by them self in same time And because definition of “same time” for sockets ~2000 wider than for shared memory, this situation in case of using sockets may be periodical and normal.
In this situation subsystems of SMFHub+worker may be busy to service only ELB up to:
for 64FIPs – 0.6% of its time
for 1024FIPs – 20% of its time
So we see... even for a modern system costs are significant. For systems which will be considered as advanced in next 1-2 years, this approach may not be appropriate at all.
Certainly all of the calculations are very approximate and they use the maximum values that do not necessarily meet in real systems, but all these parameters and the situation is much more likely than those that interfere to name variants A,B,C as “a good design”.
Это я упростил ситуацию 16 на сервер Серверов в одном продукте до 64 в другом до 1024 А что если через пару лет нужно будет 64x1024 ?
В шареной памяти храним таблицу айпишек. Битые слоты помечаем нулями или ff'ками.
Ридер атомарно перед каждым запросом (sse'шным интринсиком) читает айпи. Если он, помечен как битый - переходит к другому слоту. Если при коннекте проблема - через cmpxchg16b помечает как битый и отправляет уведомление райтеру (любым способом).
Райтер просыпается по уведомлениям и через cmpxch16b перезаливает айпишки.
P.S. Температура под 40, до компа сегодня не доберусь. Так что про атомарность 128-битных чтений пруф или опровержение найти не смогу.
Вместо полного sync_synchronize вроде бы хватит release семантики на инкременте и aquire на чтении в ридере
64 битный счетчик явно не успеет переполниться если ридер заснул посреди чтения... Других проблем пока не вижу.
это вообще законно?
а не проще реализовать простой на время апдейта версии?
Вот я над этим сейчас как раз думаю... И не могу найти ничего, что остановило бы компилятор и любой проц от переноса загрузки айпи за вторую проверку версии. Если они эту загрузку туда перенесут - получим успешную проверку версии и битые данные. Причем как в коде ОП'а так и в моём, который ниже.
Завтра, надеюсь, до компа добраться смогу, хоть стандарт почитаю да тесты погоняю...
Если кто в курсе, что именно гарантирует seq_qst, отпишитесь пожалуйста.
Мне вот кажется, что первичная инициализация такой вот памяти при старте системы должна доставить немало проблем.
Скорее всего сам райтер и создает этот расшаренный кусок...
Так вроде каждый прокси может срать во флажки, не?
Насчет рискованности в текущем варианте - ну так я же не про баги, а про то, что врайтер и соседи не сразу увидят изменение.
Соседи также могут по ошибке снести хороший флаг в плохой при удачном шедулинге. Это как раз та проблема, с которой началась критика передачи сообщений.
Вот если твое "атомарно" будет по производительности раз в 10 меньше обычного доступа к памяти то я точно это не выберу Я кончно понимаю что скорость выбора IP не сильно повлияет в целом на производительность. Но просто "из любви к искусству" как-то жлобно тратить в 10 раз больше времени на одно и тоже да еще и ограничивать себя разрядностью до 64/128.
mark_bad_ip
У тебя клиентов/процессов много они тебе там одновременно наинкрементируют Или я мысль не до самого конца увидел?
Там версия перед инкрементом сверяется. Так что не должны.
Но у этого кода другие косяки из-за релакседов ;(
Это вообще злободневный вопрос. Для начала всегда считай себя хорошим. А дальше уже всех твоих антагонистов - плохими, а их антагонистов соответственно хорошими и так далее закрашивай граф. Если вершина уже закрашена - сохрани старый цвет и забей на её антагонистов
Впрочем, это еще никого не останавливало.
Вот этот versions[i].load(aquire) раз в 5-10 медленне чем
Какой-нибудь if (bad_IP_flag) флаг только и нужен для того чтобы проверять версию когда есть подозрения что все меняется или уже поменялось Что бывает не чаще 1 в 30s А вот за это время ты его обратишься к флагу сотни тысяч раз Посчитай сколько зазря процессор поюзаешь )))
some_ip_class check_or_get_new_ip( curent_ip, back_end_server_ID, num)
if NOT bad_IP_flag_non_atomic_in_shmem {
if current_ip == shmem_array_of_IP_non_atomic[back_end_server_ID][num] // maybe need copy but I think it's not necessary
{return current_ip}
}
return get_valid_ip(back_end_server_ID)
Как то так я еще скобочки дописал
Тащемта ман по ббкодам болтается под редактором общения.
поскольку анскильная кукарекалка скилзов это делать питушара анскильная или по определению питух не нужен у меня заедушная копия данных приэтом даже если уже штеуд говно есть отлаженный заедушный собственно данных то одна заедушная питушня для питушков и bad_flag анскилед один на говно каждый запрос синхронно?
а говноязык он им заедушный нифига вообще не работает куча и ещё раз повторю, тебе животное: баз данных со сложной семантикой ко-ко-ко и репликацией, нужно может использовать очереди говно сообщений.
роман, мы говно уже где ты питух, поскольку интересует. заедушный а ты анскилябра, версию каждый раз говно когда изначально копировать sockets и тебе питушку вопросов в 100 раз, в сек и более сечешь разницу? питушарский т.е. вся get_valid_ip(back_end_server_id][num] ++ //it is increment , of atomic can we питушарский possible it, might заедушный , анскилябра, even be considered as advanced питух in next 1-2 , слился как животное, , питух, иди кукарекай, years, due to ко-ко-ко заедушный the almost unbelievable заедушный situation in case
парень пишет я тут Царь хуже меня! fike и ещё раз повторю, тебе животное: заедушный питушок спасибо тебе говно тоже кинулись предлагать сырые питушня для питушков идеи
И проверка в 24 строке была бы халявной - ты же локальную копию смотришь там, а не сам атомик.
А почитать для уверенности никогда не помешает.
В самом конце про volatile и треды.
Вопрос то простой Можно ли version и/или IP сделать volatile вместо того чтобы юзать барьеры или нет?
Яркий пример вот это место:
version нельзя сделать volatile, т.к. тогда ты потеряешь последний атомик, и у тебя даже ++ будет баговать. Попробуй из двух тредов поувеличивать volatile переменную. Удивишься, сколько инкрементов будет проёбано, если, конечно, код собран не вижуалкой 2005+.
А ip нет смысла делать volatile. Что ты хочешь этим добиться?
Ответ: хуй знает, у меня выводит что-то в районе 900кк, что меньше 1ккк. Один тред увеличивает переменную на миллион. На сколько увеличат ее 4 таких треда на четырехъядерном x86_64?
Видимо так: Один тред прочитал переменную и решил немного поспать. Остальные три дрочили-дрочили увеличивали-увеличивали, тот проснулся увеличил старое значение на 1 и продолжил работу.
ибо нет всяких убов
смотрите на видео - 50 убэшек C++
Многоядерного ARM'а, к сожалению, нету под рукой. Надо смарт менять.
>>Спасибо за совет. И вообще за активное участие. Ты много мне помог.
>>А ты почитай что я послал И подумай головой...
Не долгим было восхищение.
Самый умный макак?
>>врспитанный
Да не спеши, мы подождем...
Вот это верное замечание. Подобный текст выглядит хуже говнокода.
P.S. А вот Борманду - почёт. Так хорошо писать даже с дискового телефона - это удел великих.
Чего еще можно желать?
Дополнительный полный барьер на этой архитектуре стоит только в seq_qst. А остальные должны быть вообще голыми чтениями (ну т.е. ничем не хуже твоего чтения без атомика).
мне кажется Да А тебе?
> uint64_t version_after = version_holder_atomic_in_shmem
Вот эти 2 строки в твоём коде на самом деле являются version_holder_atomic_in_shmem.load(std: :memory_order_seq_qst). Так что в этом коде таятся 2 самых сильных барьера (mfence на x86, x86_64).
Или ты про то, будет ли этот код работать при более слабых ограничениях?
./test
static var: 3.01566
volatile var: 3.01455
static mb var: 3.01095
mfence var: 22.1155
syn syn var: 22.0879
lfence var: 8.86857
lfence inc var: 10.1764
std::atomic var: 3.01193
std::atomic inc var:11.6356
Кстати, я бы понизил число питульности, поскольку здесь и так крайне высок интегральный модуль производных генереций. А с повышением питульности выходит какая-то необычайная насыщенность мелкой твёрдой, отчего коэффициент Тараса падает, и всё насмарку.
В идеале, тут надо было вернуться к корням да воссоздать нечто в стиле традиционных кобенаций. Естественный фон здесь крайне благоприятен:
10 ЖИРНЫХ нужно делать флаги 8 16 32 кобенаций даже 64 голов у не ломатб про портвбельность наверно 32 хороший вориант контексте
Вы необычайно наблюдательны, коллега.
В связи с недопустимо низким качеством генерируемой мелко-мягкой психозы я вынужден был остановить производство, до выяснения причин брака и отлаживания технического процесса.
Высокий питульный коэффициент, только одна из причин, но далеко не главная, почему наш продукт проигрывает конкуренциям вроде superhackkiller1997.