2018-09-29 виртуални машини, кеш, cgroup-и и други гадости

September 29th, 2018 by Vasil Kolev

(английската версия е в блога на StorPool)

Това е един случай, който отне известно време да се дебъгне, включващ StorPool, виртуални машини, cgroup-и, OOM killer-а и кеширането. Части от него би трябвало да са полезни и на други хора/админи.

Започна с оплакване от клиента:

> Пак се случва, на тоя hypervisor виртуалните машини ги убива
> OOM killer-а. Това не се случва на hyervisor-и, на които няма
> cgroup-и, не може ли да не ги конфигурирате?

(
Смисълът изобщо да имаме cgroup-и е, че няма друг начин да се резервират памет и процесори за даден набор процеси. Понеже storage системата е една от най-чувствителните части на системата към латентности и подобни проблеми (или, нека сме по-точни, всичко останало е много чувствително към проблеми на storage системата), доста е полезно да е предпазена от всичко останало. По същия начин е полезно и да се разделят виртуалните машини и системните процеси, за да не го отнесе грешното нещо при някой memory leak или побъркан allocation. Също така, има много забавен вариант за memory deadlock (който го има с всяка storage система, която не е в kernel-а и с някои които са вътре, който може да бъде описан по следния начин:

Процес към kernel-а: искам памет
Kernel към процеса: ей-сега
Kernel (говори си сам): то искаш, ама няма. Нямам какво да освободя друго, но мога да flush-на някакъв dirty cache
Kernel към storage системата: на ти тия данни, запиши ги
Storage системата към kernel-а: разбира се, за теб си режа даже ноктите без упойка
Storage системата (говори си сама): тия данни не са aling-нати както трябва за гнусния хардуер отдолу, трябва да ги копирам малко и наместя
Storage системата към kernel-а: искам памет
)

Разбира се, цялото нещо нямаше да е чак такъв проблем, ако Linux-кия OOM killer и cgroup-ите работеха правилно, но версиите по всичките kernel-и, които срещаме (което значи CentOS[67], т.е. kernel с име 3.10.xxx и с diff спрямо оригинала, който вероятно е колкото 30% от кода) се държат странно и от време на време застрелват sshd вместо който трябва. Новите идеи за отношенията м/у OOM killer-а и cgroup-ите се очертава да ни стъжнят живота още повече.

Та, за да си резервира човек някакъв набор памет за набор от процеси на KVM hypervisor, трябва да създаде memory cgroup-а за системните неща (system.slice), виртуалните машини (machine.slice), може би user-ските неща (user.slice), и в нашия случай storpool.slice. После за всички тия групи сборът на лимитите трябва да е с около 1-2GB по-малък от общата памет (понеже някаква част си е за kernel-а и той никъде не я account-ва), и да се подсигури, че всички процеси са по тези cgroup-и или техни деца, и няма никой в root cgroup-ата. Това се постига с разни опции на libvirt, systemd и малко тел+тиксо, но като цяло върши работа.

Има обаче известен проблем с memory cgroup-ите, буфер кеша и OOM killer-а. Ако не ползвате cgroup-и и не ви стига паметта, kernel-ът по принцип flush-ва dirty page-овете (т.е. незаписаните данни) и clean cache (прочетени файлове от файловата система), та да си върне памет и да може да я даде на който я иска. В случая с cgroup-ите обаче clean cache не се почиства, и предпочитания за kernel-а начин е просто да пусне OOM killer-а, който да застреля някой полезен процес.

(За който бори такива проблеми, има доста полезна информация колко памет е account-ната за какво в “memory.stat” за всяка cgroup-а в /sys, например /sys/fs/cgroup/memory/machine.slice/memory.stat)

Ако си говорим принципно, в случая с виртуалните машини това не трябва да е проблем, понеже те няма причина да ползват кеш – вътре във виртуалката ще има какъвто и трябва, и няма смисъл да се хаби двойно памет (съответно всичките дискове се настройват с cache=none). Да не говорим, че не-спирането на кеша (който разбира се е пуснат по default, ама post за идиотските default-и в qemu/libvirt ще е бая) не позволява да се правят live миграции (libvirt-а отказва, щото можело да доведе до загуба на данни).

(Което всъщност е оправено в https://github.com/qemu/qemu/commit/dd577a26ff03b6829721b1ffbbf9e7c411b72378, но още не изглежда да е merge-нато, благодаря на колегите, че ми го посочиха)

Повечето оркестрационни системи в наши дни ползват “cache=none” в техните конфигурации (и интеграциите на StorPool с тях гледат да го настроят, ако има как), но в този конкретен случай системата имаше някакви много стари виртуалки, правени от стари template-и (някои от които ползваха IDE вместо virtio), и със съответния default. Правилното решение за тези виртуалки би било да се оправи template-а и да се рестартират, но по някаква причина хората не са щастливи да рестартират виртуалките на клиентите, и предполагам, че и клиентите не са големи фенове на идеята. Също така имаше някаква странна причина (която мозъкът ми е изтрил) да не може да се сменят конкретно тези template-и.

Не сме първите, които удрят проблема с “твърде много clean cache в паметта, който не ни трябва”. Ето какво ни хрумна и какво направихме в крайна сметка:

Първата идея, която ни хрумна беше периодично да почистваме buffer cache, с “echo 3 > /proc/sys/vm/drop_caches”. Това ще сработи, но като решение е доста тъпа брадва, понеже и ще изхвърли от кеша полезни неща (и системата ще си препрочита libc-то постоянно).

Втората идея се появи с това, че има нещо много хубаво, наречено LD_PRELOAD, с което в общи линии може да се прихване всякаква функция, която се вика от дадено binary и да се добави още нещо. По този начин може да се прихване open() и ако се открие, че е block device, да му се сложи флаг O_DIRECT (който в общи линии значи “опитай се да не ползваш buffer cache”). Проблемът на O_DIRECT е, че има някои неприятни ограничения, като например изискването паметта и offset-ите при писане/четене да са подравнени по някакъв начин, като 512 байта подравняване би трябвало да са ОК, ако не се ползва файлова система (където може да се наложи подравняване на page size или повече). Понеже няма как да знаем какво прави виртуалната машина отгоре, имаше шанс да се наложи да прихващаме всички read() и write() и да правим копие на данните в наша, подравнена памет, което щеше да е прилично количество писане и щеше да е трудно да няма грешки.

Сетихме се също така, че в kernel-а има интерфейс, наречен posix_fadvise(), който може да се използва да маркира някаква част от кеша като “няма да ми трябва повече” (които kernel-а да разкара). Това можеше да се използва с LD_PRELOAD за read()-ове, като просто се маркираше прочетеното като POSIX_FADV_DONTNEED. Идеята я имаше донякъде реализирана в https://code.google.com/archive/p/pagecache-mangagement/ и тръгнах да я дописвам да прави нещо по-просто (просто posix_fadvise() веднага след read, вместо сложни сметки с колко кеш да се позволява на процес).

Някъде в тоя момент CTO-то ни попита “а то всъщност трябва ли да се вика posix_fadvise() от процеса, или може от всякъде”? Оказа се, че в същото repo има прост инструмент, който изхвърля от кеша данните за даден файл (или блоково устройство), наречен “fadv” (който открих след като написах същите 5 реда).

Крайният резултат беше малък скрипт, който пуска “fadv” за всички наши устройства и ги държи извън кеша, което се каза приемлив workaround. Оказа се и доста бърз – на първото си стартиране му отне около минута да изхвърли около 100GB от кеша, на следващите си пускания минаваше за под секунда.

2018-09-22 бавни ли са ни базите данни?

September 22nd, 2018 by Vasil Kolev

(обмислям лекция за OpenFest по темата разни инструменти за debug-ване, които съм открил в последно време, звучи ли интересно на някого?)

Тия дни около една лекция на hack conf за scale-ването на приложения, разговори с разни хора за архитектура на системи и дебъгването на някакви готови неща почна да ми се мотае следния въпрос: наистина ли базите данни са толкова бавни и защо?

Въпросът идва от идеята, че единият начин да си спестиш натоварването в пиковите моменти на базата е да буферираш в някаква опашка (напр. Apache Kafka) заявките, така че да може да наваксаш в някакви по-тихи периоди. Това го чух и като идея изобщо за влизащите заявки в системата, без значение дали са batch заявки или такива директно от потребители.

За да не се губят данни, тази опашка трябва да persist-ва данните, т.е. реално да представлява един transaction log, който да се пише на диска преди да се отговори на клиента, че данните са получени. За да се приемат тези данни, вероятно е нужна и малко валидация, да се види дали са “приемливи”/отговарят на constraint-ите на системата.

… което е точно каквото прави всъщност базата данни. По това, което съм виждал (и чел в “Transaction processing”, което май все още е книгата “как се пише база данни”), базата прави точно това – проверява няколко неща и пише транзакцията в лога. Промяната на реалните данни се случва на batch-ове, като се затвори текущия сегмент от transaction log-а, така че писането в базата би трябвало да е бая бърза операция. Четенето се случва от кеш или от реалните данни, така че ако transaction log-а не е на същия хардуер, като цяло няма обективна причина опашката да помага, реално е вършене на същата работа още веднъж.

Та явно базите данни са по-бавни, отколкото би трябвало. Някой да има наблюдения по темата?

2018-09-16 европейско DST

September 16th, 2018 by Vasil Kolev

След дълго гласуване, Европейската комисия ще предложи да се разкара DST (daylight savings time), смяната на времето, вероятно от 2019. Което е прекрасно и вярна стъпка по пътя всички да сме по UC и да не се занимаваме с глупости.

По случая някакви хора се оплакват, че трябва да се променя някакво количество код, за да се вземе предвид тази промяна. Което е тотална идиотщина, понеже за който не знае, timezone файла/базата се променя ПОСТОЯННО, най-лесно може да се види от архива на tz-announce листата на IANA, където се казват новите release-и, или github repo-то на tz базата, където има още по-подробна информация. Само някой, дето не му се е налагало никога да се занимава с time zone-и може да си мисли, че те са нещо супер статично и ясно, и че махането на европейското DST е някаква сериозна и “опасна” промяна.

(или който не е виждал как даже debian пъхат тоя update във всеки minor release, въпреки че не е свързан със security)

Та, има още стъпки по темата, но махането на европейското DST, и в един момент – единна европейска времева зона, биха били добро начало да проимаме някаква свястно и глобално използваемо време.

2018-09-13 държавен open source

September 13th, 2018 by Vasil Kolev

Както някои хора знаят, вече имаме закон, който казва “каквото се пише за държавата, трябва да е open source и version control-а му да е публичен”. Също така, в момента порталът за отворени данни се пренаписва в две части, и след някакво ходене по мъките едната е публикувана в github, където хората могат да review-ват кода и да дават идеи.

2018-08-24 интересен FreeBSD/ZFS проблем

August 24th, 2018 by Vasil Kolev

Днес борихме забавен проблем от типа “може да се дава за пример как се дебъгва нещо, което не разбираш”.

Оригиналното оплакване беше “защо ми е толкова висок пинга до тия машини”. Проследихме го до router-а им, едно FreeBSD (скоро update-вано), което върши разни вътрешни поддържащи дейности и е router за един набор тестова железария.
(аз, ако някой не знае, от FreeBSD не разбирам и конкретно на тая машина съм и се заканил да я преинсталирам с Debian, ама ме спират)

Проблемът беше доста на random и за различни хора. Основното нещо, което се забелязваше в top беше, че 25% от процесорното време отиват в "interrupt", което в общи линии значеше един от 4те core-а на процесора (машината няма hyper-threading). Понеже ми се искаше да имам някаква по-свястна видимост, реших така и така да тествам prometheus при нас, съответно update-нах port-овете на машината (оказва се, че node_exporter-а на prometheus вече го има там), сипах една виртуалка в офисния клъстер, сипах един prometheus и го вързах в grafana-та ни.
(все повече и повече обичам секундните статистики, не знам дали съм го казвал)

Проблемът с 25-те процента в interrupt си се виждаше много хубаво на графиките, и не корелираше с мрежов трафик или каквото и да е. След като си задавах въпроса “какъв е еквивалента на perf top във FreeBSD” открих един много полезен сайт с DTrace примери и по-специално hotkernel програмчето, което можеше да ми каже за даден период от време къде е висял kernel-а. Оказа се, че освен двете idle функции, основно се стои в vm_page_scan_contig(), т.е. някой усилено се опитваше да си заделя последователна памет. Нямаше някъде информация за някакъв известен такъв бъг (и се надявам google да индексира това за следващия, който търси).

Някъде в този момент се оказа, че backup-ите ни, които ходят до тази машина през един NFS вървят доста бавно – сетихме се от мрежовия проблем. В един момент колегата каза “абе, я да видим ако спрем писането им за малко дали нещо ще се промени” и изведнъж проблемът изчезна. Последва въпросът “това от NFS-а ли е, или от ZFS-а отдолу?”, което лесно се тества с едно копиране отвън, и се оказа, че този път (за разлика от доста други) NFS-ът е невинен.

Та, оказахме се с проблем, че като се опитваме да пишем по ZFS-а, той се опитва да си заделя памет, това го бави много и всичко върви зле, а докато kernel-а му търси памет, всичко, дето се опитва да се schedule-не на тоя процесор (като например прекъсванията на една от опашките на мрежовата карта) страда.

Ровихме разни неща по ZFS-а, и в един момент около размислите по паметта видяхме, че от 16GB памет 14GB се води "wired", т.е. заета от kernel-а за някакви неща, в случая изглеждаше да е за ARC кеша на ZFS. Решихме да я намалим, като и пипнахме vfs.zfs.arc_max на 10GB, което обаче нямаше ефект. С малко ровене открихме, че “то няма начин да се чисти кеша, но ако някое приложение си поиска памет, ще се flush-не”, съответно докато си извадя C програмката, дето яде памет, колегата драсна следното:


python -c 'a="b"*1024**4'

(което реално се опитва да изяде 1TB памет, та трябваше да го утрепем в един момент)

Съответно в момента, в който се появи някаква свободна памет, проблемът спря да се появява. Накратко казано, default-ната стойност за количество кеш на ZFS-а е толкова висока, че изяжда цялата памет, и прави всичките му нужни memory allocation-и много бавни, което потрошава цялата производителност. Вероятно това е някаква скорошна промяна, та се чудим дали/къде да го report-ваме.

Update: Разбира се, някой трябва да чете документация – sysctl-то трябва да отиде в loader.conf, преди да се пали root-а и т.н., понеже ZFS-а тия неща ги поглежда само докато си пуска pool-а и после не ни обръща внимание. С един reboot нещата се оправиха…

OpenFest 2017 network write-up

August 20th, 2018 by Vasil Kolev

Това иначе никога няма да го кача, та ето го като dump от оригналния документ, ако някой му се занимава да го преведе до смислен html да каже да му дам link към оригинала.

OpenFest 2017 network write-up

Sysadmin day 2018

July 18th, 2018 by Vasil Kolev

Стандартното празнуване на деня на системния администратор – следващия петък (27ми юли), в “Кривото” на ъгъла на “Дондуков” и “Будапеща”.

Update: бях отворил единична кавичка и затворил двойна, което беше строшило вида на целия блог…

2018-07-10 “Proofs and refutations” на Имре Лакатош

July 10th, 2018 by Vasil Kolev

Тия дни прочетох “Proofs and Refutations” на Имре Лакатош (Imre Lakatos), унгарски математик и математически философ.

Книгата ми хареса много, понеже ми показа процесът на развиване на една теорема и доказателствата ѝ, пътят по който се е стигнало до нея (или терминът може би е ориентираното дърво, не точно път, понеже има и доста от задънените улици). За разлика от повечето ми учебници по математика, книгата ми дава идея защо дадено доказателство се развива по този начин, защо са избрани тези дефиниционни множества и други начални моменти, и въпреки формалното обяснение да покаже практическото/разбираемо такова.
(един учебник с толкова информация вероятно щеше да е около 5 пъти по-дебел, но щях да го чета с удоволствие)

Като погледна назад към коя математика ми е останала в главата и коя не, разделителят в общи линии изглежда като да съм си отговорил на въпроса “защо” (я има|се ползва), понякога и “защото е тривиална”. Голяма част от останалото е отказало да ми се лепне в главата, и понякога ми се е налагало по-късно да се ровя в него и да се опитвам да разбера, когато ми е трябвало (и когато много често съм се спирал на изцяло приложната му страна, без да задълбавам (полета на Галоа) , или просто да придобия бегла представа (решетки, които още не мога да докарам до някакво интуитивно ниво)).

Остават разни интересни въпроси:

– Грешно ли се преподава математика? Явно не съвсем, при условие, че една прилична част все пак съм я научил (и ако трябва, мога да си изведа формулата за решение на квадратно уравнение, например). Дали може да се подобри по смислен начин?
– Ако хората имат нужда от повествование (narrative, разказ), за да запомнят/схванат нещо, това дали не е бъг в главите ни, който трябва да оправим, или нещо, от което трябва да се възползваме?
– Някой да знае учебници, които обясняват по този начин различни математики? Не говоря за безумно глупавите от типа на “head first statistics”, а някакви, които могат да опишат процесът на извеждане на текущата теория, причините за това и по какво се работи в момента, може би комбинация от математика и история (без премълчаванията, за които Лакатос пише)?

Интересно също кои са наследниците на Лакатош. Карл Попът му е предшественик, но да речем, че неговите идеи достатъчно са се просмукали в каквото друго съм чел, и ми е интересно какво ново (и смислено) има в областта в последните 50-тина години…

p.s. ако някой знае къде има книгата на Лакатош на български, да ми пише, има някакви хора, на които искам да я дам и които няма как да я четат в оригинал.

2018-06-30 две малки парчета код

June 30th, 2018 by Vasil Kolev

Тия дни ми се случи да напиша две малки парчета код, които да ми решат някакъв проблем.

Първото е sproxy – малък демон, който чете на stdin, пълни един цикличен буфер и го дава на всеки, който се закачи за него. Използвам го, за да заместя multicast-а във FOSDEM-ските videobox-ове – видео потокът от encoder-а в кутията се налива в sproxy-то и си тече, и консуматорите (локалния recorder, voctomix-а, разни дебъгващи хора) могат да се свържат до него и да си дърпат видеото. Сумарно стана около 120 реда код на C – кратко, изчистено и доста просто.
Остана да му допиша още един порт, на който като се върже клиент, получава бързо всичко, което е в буфера (в момента който се свърже, получава всичко ново), за малкия tool по кутиите, който взима текущия кадър от видеото да го покаже на дисплея.
(писах го няколко часа и го дебъгвах още толкова)

А по време на първите лекции на БургасConf си сглобих нещо малко ffmpeg с ebur128 филтъра, малко питонски код, InfluxDB и Grafana, което в сравнително реално време да ми чертае нивото на звука от stream-а (примерен изглед). Цялата работа опря до един ffmpeg команден ред, един regex и една функция, която сипва данните в influx-а.
(в grafana-та има и един “километраж”, което е може би най-безсмисления тип визуализация, сложих го като демонстрация. Понеже не се оправя с отрицателни стойности, нещата, дето показва са +70db)
Нещото се оказа толкова лесно и удобно, че даже го показах в един lightning talk на БургасConf. С още малко доработка мисля да влезе в monitoring-а на FOSDEM и разните други конференции, които правим. Ще го кача след доработката, че сега никак не ме радва как работи и как няма буфериране, ако базата почне да се бави.

Такива неща са пример колко полезно и важно е човек да може да пише поне малко, понеже с малко код и съществуващия софтуер човек може да постигне интересни неща. Ето за това не мога да смятам системен администратор, който не може да пише за истински такъв. Чудя се мога ли да измисля някакъв курс за писане на дребни хакове :)

2018-06-23 Национална конференция на “Да България”

June 23rd, 2018 by Vasil Kolev

Днес се проведе първата Национална конференция на “Да България”.

Обичам заниманията с неща, които други не са правили. Днес това беше реализираното електронно гласуване за всичките точки от дневния ред, което изглежда е първото такова нещо, правено в България, като малка демонстрация, че е нещо, което всъщност може да работи. В рамките на няколко часа бяха направени 45 гласувания, едно от които с интегрална бюлетина. От наблюденията ми на подобни неща (например на учредяването на партията), дори с перфектна организация и много преброители няма как едно гласуване да е под 10 минути, а тук се справяхме за 2-3. Цялата технология беше разработена от няколко човека (от приложението до backend-а) и издържа съвсем спокойно на цялото натоварване без на сървъра да му мигне окото.

В периода, в който вървеше live stream нямаше гласувания, но се надявам в някой момент да можем да публикуваме запис от протичането на едно примерно гласуване – режисьорската работа какво кога къде да показвам на моменти ми идваше много, особено при темповете, с които работехме към края, когато всички вече бяха свикнали със задачите си.

Не беше най-уморителното събитие, в което съм участвал (OpenFest и FOSDEM определено са по-страшни, а gaming турнира в рамките на Animes Expo беше в порядъци по-неприятен), но определено ще имам нужда да си почина малко.

2018-06-12 Article 13 Take Action Day

June 12th, 2018 by Vasil Kolev

Днес е Take Action Day за чл. 13 по новата директива по темата авторски права и Internet, за която писах и преди.

Сайтове по темата – saveourinternet.eu, FB страница, twitter.

Писнало ми е да обяснявам на някакви хора, че не трябва и не може да се цензурира internet-а, но явно трябва да се повтаря от време на време.

2018-06-11 БДЖ

June 11th, 2018 by Vasil Kolev

Не мога да си обясня как БДЖ не са фалирали.

Преди месец и нещо ходих към Бургас, където бяха Елена и звяра, и понеже тя се притесняваше от многото катастрофи по онова време по Тракия, ме помоли да си взема влака. Той пътува около 6 часа, и беше идеално време за четене.

Самият влак беше приятен и доста удобен, но интересното беше с билетите – първото ми откритие беше, че за online продажба има сумарно 12 билета. После, на гарата казваха “за тоя влак местата свършиха”, а влакът определено не беше пълен, нито се напълни. Изводът, до който стигнах с малко гледане е, че хората нямат online сисема, а всяка гара по пътя има раздаден набор от места и тя може да си ги продаде до където реши, но не могат да се използват от някоя гара, където има по-голямо търсене, т.е. те ВСЕ ОЩЕ нямат online система за резервиране/продаване на места.
(нещата са толкова объркани, че наскоро се оказа, че от касите в София (щото online местата вече бяха свършили) човек не може да си вземе и билет за обратната посока)

Това ми е супер учуващо, понеже технологията за тия неща я има почти от времената, в които бях малък (една централна база данни и някакъв приличен хардуер). Комуникацията те си я имат по принцип – БДЖ още от едни много отдавнашни времена имаше паралелна комуникационна мрежа и не мисля, че навръзването на всички гари към централната система ще е особен проблем, просто защото те и сега имат някаква връзка, иначе нямаше да могат да си управляват влаковете.

Също ми е учудващо, че откакто има системата за online купуване на билети, в нея има още 1-2 нови маршрута (в сравнение с момента, в който я пуснаха), а не цялата им мрежа. Може би някой трябва да ги довлачи в текущия век?

2018-05-03 python, multiprocessing, thread-ове и забивания

May 3rd, 2018 by Vasil Kolev

Всеки ден се убеждавам, че нищо не работи.

Открих забавен проблем с python и multiprocessing, който в момента още не мога да реша чий проблем е (в крайна сметка ще се окаже мой). Отне ми прилично количество време да го хвана и си струва да го разкажа.

Малко предистория: ползваме influxdb, в което тъпчем бая секундни данни, които после предъвкваме до минутни. InfluxDB има continuous queries, които вършат тази работа – на някакъв интервал от време хващат новите данни и ги сгъват. Тези заявки имаха няколко проблема:
– не се оправят с попълване на стари данни;
– изпълняват се рядко и минутните данни изостават;
– изпълняват се в общи линии в един thread, което кара минутните данни да изостават още повече (в нашия случай преди да ги сменим с около 12 часа).

Хванаха ме дяволите и си написах просто демонче на python, което да събира информация за различните бази какви данни могат да се сгънат, и паралелно да попълва данните. Работи в общи линии по следния начин:
– взима списък с базите данни
– пуска през multiprocessing-а да се събере за всяка база какви заявки трябва да се пуснат, на база на какви measurement-и има и докога са минутните и секундните данни в тях;
– пуска през multiprocessing-а събраните от предния pass заявки
– и така до края на света (или докато зависне).

След като навакса за няколко часа, успяваше да държи минутните данни в рамките на няколко минути от последните секундни данни, което си беше сериозно подобрение на ситуацията. Единственият проблем беше, че от време на време спираше да process-ва и увисваше.

Днес намерих време да го прегледам внимателно какво му се случва. Процесът изглежда като един parent и 5 fork()-нати child-а, като:
Parent-а спи във futex 0x22555a0;
Child 18455 във futex 0x7fdbfa366000;
Child 18546 read
Child 18457 във futex 0x7fdbfa366000
Child 18461 във futex 0x7fdbfa366000
Child 18462 във futex 0x7fdbfa366000
Child 18465 във futex 0x7fdbf908c2c0

Това не беше особено полезно, и се оказа, че стандартния python debugger (pdb) не може да се закача за съществуващи процеси, но за сметка на това gdb с подходящи debug символи може, и може да дава доста полезна информация. По този начин открих, че parent-а чака един child да приключи работата си:


#11 PyEval_EvalFrameEx (
f=f@entry=Frame 0x235fb80, for file /usr/lib64/python2.7/multiprocessing/pool.py, line 543, in wait (self== 1525137960000000000 AND time < 1525138107000000000 GROUP BY time(1m), * fill(linear)\' in a read only context, please use a POST request instead', u'level': u'warning'}], u'statement_id': 0}]}, None], _callback=None, _chunksize=1, _number_left=1, _ready=False, _success=True, _cond=<_Condition(_Verbose__verbose=False, _Condition__lock=, acquire=, _Condition__waiters=[], release=) at remote 0x7fdbe0015310>, _job=45499, _cache={45499: < ...>}) a...(truncated), throwflag=throwflag@entry=0) at /usr/src/debug/Python-2.7.5/Python/ceval.c:3040

Като в pool.py около ред 543 има следното:


class ApplyResult(object):

...

def wait(self, timeout=None):
self._cond.acquire()
try:
if not self._ready:
self._cond.wait(timeout)
finally:
self._cond.release()

Първоначално си мислех, че 18546 очаква да прочете нещо от грешното място, но излезе, че това е child-а, който е спечелил състезанието за изпълняване на следващата задача и чака да му я дадат (което изглежда се раздава през futex 0x7fdbfa366000). Един от child-овете обаче чака в друг lock:


(gdb) bt
#0 __lll_lock_wait () at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:135
#1 0x00007fdbf9b68dcb in _L_lock_812 () from /lib64/libpthread.so.0
#2 0x00007fdbf9b68c98 in __GI___pthread_mutex_lock (mutex=mutex@entry=0x7fdbf908c2c0 ) at ../nptl/pthread_mutex_lock.c:79
#3 0x00007fdbf8e846ea in _nss_files_gethostbyname4_r (name=name@entry=0x233fa44 "localhost", pat=pat@entry=0x7fdbecfcb8e0, buffer=buffer@entry=0x7fdbecfcb340 "hZ \372\333\177",
buflen=buflen@entry=1064, errnop=errnop@entry=0x7fdbecfcb8b0, herrnop=herrnop@entry=0x7fdbecfcb910, ttlp=ttlp@entry=0x0) at nss_files/files-hosts.c:381
#4 0x00007fdbf9170ed8 in gaih_inet (name=, name@entry=0x233fa44 "localhost", service=, req=req@entry=0x7fdbecfcbb90, pai=pai@entry=0x7fdbecfcb9f0,
naddrs=naddrs@entry=0x7fdbecfcb9e0) at ../sysdeps/posix/getaddrinfo.c:877
#5 0x00007fdbf91745cd in __GI_getaddrinfo (name=name@entry=0x233fa44 "localhost", service=service@entry=0x7fdbecfcbbc0 "8086", hints=hints@entry=0x7fdbecfcbb90, pai=pai@entry=0x7fdbecfcbb78)
at ../sysdeps/posix/getaddrinfo.c:2431
#6 0x00007fdbeed8760d in socket_getaddrinfo (self=
, args=) at /usr/src/debug/Python-2.7.5/Modules/socketmodule.c:4193
#7 0x00007fdbf9e5fbb0 in call_function (oparg=
, pp_stack=0x7fdbecfcbd10) at /usr/src/debug/Python-2.7.5/Python/ceval.c:4408
#8 PyEval_EvalFrameEx (
f=f@entry=Frame 0x7fdbe8013350, for file /usr/lib/python2.7/site-packages/urllib3/util/connection.py, line 64, in create_connection (address=('localhost', 8086), timeout=3000, source_address=None, socket_options=[(6, 1, 1)], host='localhost', port=8086, err=None), throwflag=throwflag@entry=0) at /usr/src/debug/Python-2.7.5/Python/ceval.c:3040

(gdb) frame 3
#3 0x00007fdbf8e846ea in _nss_files_gethostbyname4_r (name=name@entry=0x233fa44 "localhost", pat=pat@entry=0x7fdbecfcb8e0, buffer=buffer@entry=0x7fdbecfcb340 "hZ \372\333\177",
buflen=buflen@entry=1064, errnop=errnop@entry=0x7fdbecfcb8b0, herrnop=herrnop@entry=0x7fdbecfcb910, ttlp=ttlp@entry=0x0) at nss_files/files-hosts.c:381
381 __libc_lock_lock (lock);
(gdb) list
376 enum nss_status
377 _nss_files_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
378 char *buffer, size_t buflen, int *errnop,
379 int *herrnop, int32_t *ttlp)
380 {
381 __libc_lock_lock (lock);
382
383 /* Reset file pointer to beginning or open file. */
384 enum nss_status status = internal_setent (keep_stream);
385

Или в превод – опитваме се да вземем стандартния lock, който libc-то използва за да си пази reentrant функциите, и някой го държи. Кой ли?


(gdb) p lock
$3 = {__data = {__lock = 2, __count = 0, __owner = 16609, __nusers = 1, __kind = 0, __spins = 0, __elision = 0, __list = {__prev = 0x0, __next = 0x0}},
__size = "\002\000\000\000\000\000\000\000\341@\000\000\001", '\000' , __align = 2}
(gdb) p &lock
$4 = (__libc_lock_t *) 0x7fdbf908c2c0

Тук се вижда как owner-а на lock-а всъщност е parent-а. Той обаче не смята, че го държи:


(gdb) p lock
$2 = 0
(gdb) p &lock
$3 = (__libc_lock_t *) 0x7fdbf9450df0
(gdb) x/20x 0x7fdbf9450df0
0x7fdbf9450df0
: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fdbf9450e00 <__abort_msg>: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fdbf9450e10 : 0x00000000 0x00000000 0x00000000 0x00000000
0x7fdbf9450e20 : 0x00000000 0x00000000 0x00000000 0x00000000
0x7fdbf9450e30 : 0x001762c9 0x00000000 0x00000000 0x00000000

… което е и съвсем очаквано, при условие, че са два процеса и тая памет не е обща.

Та, явно това, което се е случило е, че докато parent-а е правел fork(), тоя lock го е държал някой, и child-а реално не може да пипне каквото и да е, свързано с него (което значи никакви reentrant функции в glibc-то, каквито па всички ползват (и би трябвало да ползват)). Въпросът е, че по принцип това не би трябвало да е възможно, щото около fork() няма нищо, което да взима тоя lock, и би трябвало glibc да си освобождава lock-а като излиза от функциите си.

Първоначалното ми идиотско предположение беше, че в signal handler-а на SIGCHLD multiprocessing модула създава новите child-ове, и така докато нещо друго държи lock-а идва сигнал, прави се нов процес и той го “наследява” заключен. Това беше твърде глупаво, за да е истина, и се оказа, че не е…

Около въпросите с lock-а бях стигнал с търсене до две неща – issue 127 в gperftools и Debian bug 657835. Първото каза, че проблемът ми може да е от друг lock, който някой друг държи преди fork-а (което ме накара да се загледам по-внимателно какви lock-ове се държат), а второто, че като цяло ако fork-ваш thread-нато приложение, може после единствено да правиш execve(), защото всичко друго не е ясно колко ще работи.

И накрая се оказа, че ако се ползва multiprocessing модула, той пуска в главния процес няколко thread-а, които да се занимават със следенето и пускането на child-ове за обработка. Та ето какво реално се случва:

– някой child си изработва нужния брой операции и излиза
– parent-а получава SIGCHLD и си отбелязва, че трябва да види какво става
– главния thread на parent-а тръгва да събира списъка бази, и вика в някакъв момент _nss_files_gethostbyname4_r, който взима lock-а;
– по това време другия thread казва “а, нямам достатъчно child-ове, fork()”
– profit.

Текущото ми глупаво решение е да не правя нищо в главния thread, което може да взима тоя lock и да се надявам, че няма още някой такъв. Бъдещото ми решение е или да го пиша на python3 с някой друг модул по темата, или на go (което ще трябва да науча).

2018-03-29 ТУЕС

March 29th, 2018 by Vasil Kolev

(това трябваше да го напиша преди седмица, ама работа, умора, други оправдания)

Някога бях чувал историята (за която не намерих потвърждение и май трябва да поровя още малко), че всички ресторанти в България, без значение какви са, трябвало да предлагат шопска салата и още нещо, за да са сертифицирани, че може да работят (не помня дали другото беше пържени картофи или кебапчета). Това звучи малко странно за да речем китайски или суши ресторант…

На подобен принцип МОН искаше да придърпа ТУЕС (училище, което реално е под шапката на ТУ) към себе си и да го уеднакви с останалите (още подробности има на сайта на асоциацията на завършилите там). Аз съм от хората, които и една добра дума не могат да измислят за ТУ-София (самия университет, студентите там не са виновни за състоянието и управлението му, само са пострадали от това), но да се отнеме автономията на ТУЕС ми се вижда твърде лоша идея.

Работил съм с хора, завършили къде ли не – random ПМГ-та, езикови гимназии, СМГ, НПМГ, ТУЕС и НПГ-КТС в Правец (което май вече са го придърпали), и ако трябва да избирам с хора откъде трябва да работя, поне за момента ТУЕС е на първо място. Хората от там имат желание да вършат работа и им е интересно, което изглежда да е доста по-рядко срещано в останалите. Заради това и написахме от името на ФОП (фондацията зад OpenFest) писмо по темата. Ходих и преди седмица да помагам (съвсем малко) във видеото и т.н. на HackTues и в един момент, гледайки какво става там реших, че това изглежда като правилната гимназия, в която Иги да иде да учи, като порасне…

А е късно да пиша по темата, понеже поне засега това изглежда да свърши работа и засега нещата ще са ок.

“Филтри и предразсъдъци”

March 21st, 2018 by Vasil Kolev

Около чл. 13 в петък ще се организира дискусия по темата, на 23.03 (петък) от 15:00 в Сохо (и ще се излъчва на живо). Казва се “Филтри и предразсъдъци: Технически аспекти на предварителното филтриране на съдържание онлайн”.

2018-03-17 малък видео setup

March 17th, 2018 by Vasil Kolev

Събирам (засега основно в главата си) setup за видео streaming и запис в hackerspace-овете в България. Изискванията са:

– минимална инвестиция в нов хардуер;
– (сравнително) лесно за използване (предполагам, че хората там са поне донякъде технически грамотни);
– възможност за stream-ване на текущите платформи, и може би и в тяхната си страница;
– запис/архивиране;
– поносимо качество.

Целта на setup-а е да се справи с най-простия тип събитие, което е един лектор с презентация.

Компонентите са следните:

– запис на звука – може да е от въздуха, но по-добре една брошка на лектора, + запис на залата по някакъв начин, за въпроси и т.н.;
– усилване на звука – дори в малка зала е добре да се усили звука от лектора и да се пусне на едни колони, най-малкото има feedback дали си е пуснал микрофона;
– видео запис – да се запише видеото от презентацията и може би самия лектор как говори. Това има варианта с камера, която снима лектора и екрана, или screen capture, директно от лаптопа му (или някой по-сложен setup, за който вероятно няма смисъл да пиша);
– streaming – да се извадят аудио/видео сигнала в/у някакъв протокол и да се stream-нат до някоя услуга;
– restreaming – услугата да го разпрати навсякъде и може би да го запише.

Вариантите за компоненти/setup-и в главата ми са следните:

– ffmpeg команда, която stream-ва екрана + звук от звуковата карта, в която има един свестен микрофон – това го имаме в няколко варианта, тествани и работещи (за windows и linux), трябва да ги качим някъде. Това е най-бързия начин, почти не иска допълнителен хардуер (освен един микрофон, щото тия на лаптопите за нищо не стават). Микрофонът може да е например някоя bluetooth/usb слушалка, или просто от слушалки с микрофон, да е близо до главата на лектора. Може да е от стандартните брошки, които се използват по различни събития, аз имам една китайска цифрова, дето в общи линии ме радва и е около 200-и-нещо лева от aliexpress;

– проста малка камера, която може да записва видео от екрана и звук, която може да бълва и по IP някакси. Това в общи линии са gopro-та (ако се намери как да им се пъхне звук) и още някакви подобни камери, които нямат особено добро качество (особено на звука, та задължително трябва външен микрофон), но на хората и се намират.

– проста камера, която обаче не може да бълва по IP, и има HDMI изход. Това е от нещата, които на хората им се намират по някакви причини, и в тая категория са половината DSLR-и и фотоапарати (които не прегряват след дълга (2-часова) употреба), gopro-та и нормален клас камери. Това се комбинира с устройство, което може да capture-ва HDMI и да го stream-ва, където засега опцията е един китайски device.

– streaming service – човек може да ползва youtube, моя streaming, или ако се мрази, facebook. Много места би трябвало да могат да си пуснат нещо просто при тях (например един nginx с модула за rtmp), да stream-ват до него, то да записва, и от него да restream-ват на други места и да дават някакъв лесен начин на хората ги гледат (с едно video.js/hls.js, както последно направихме за openfest).

Та, за момента основните неща, които издирвам са:

– евтини и работещи микрофони;
– евтини работещи камери с hdmi изход (или с ethernet порт, тва с wifi-то е боза), които да са switchable м/у 50hz и 60hz;
– hdmi capture вариант.

Приемам идеи, и ще гледам да сглобя едно такова за initLab.

2018-03-13 китайски лаптоп

March 13th, 2018 by Vasil Kolev

(те всичките лаптопи се правят в Китай вече, ама не ми хрумва как да го кръстя иначе)

Преди някакво време разбрах за един проект на ентусиасти от Китай за нови дъна за стари лаптопи. От много време ми липсваше 4:3 дисплея, T420 от време на време ми беше бавен (дори с 16GB памет и SSD), по-новите thinkpad-и са с гадна клавиатура, а Retro проекта в крайна сметка не беше customizable и не беше приемлив (с тая NVidia карта и широк дисплей, да не говорим за цената).

Поръчах си един t60p от ebay, и след като дойде тръгнах да си поръчвам дъното. От форума на хората и някаква facebook страница намерих контакти, писах си с един човек, който ми предложи директно лаптоп, но аз си поръчах само дъното (in hindsight, да си бях взел цял лаптоп). Няколко неща по темата с поръчването:
– опциите бяха SWIFT и western union. Не ми се разхождаше, та го направих по SWIFT, и там се оказа, че има допълнителни такси, които взимат от получателя (които не могат да вземат от мен);
– За освобождаване от митница ми поискаха следните неща: фактура (която поисках да ми издадат, щото нямаше) която включва и цената и транспортните разходи, EORI номер, пълномощно да ме представляват и документ за направеното плащане (изискване на митниците за стоки от Китай и Хонг Конг, пише “SWIFT или PayPal”);
– EORI номер може да си издадете безплатно, ако имате електронен подпис и търпение (бях си издал за нещо друго, отне около седмица);
– DHL могат да пратят как изглежда митническата декларация, да си я платите с един online превод и да си получите нещата (иначе искат 24 лв да направят превода те);

Дъното беше $780 и доставка, вариантът за това дъно с цял лаптоп (без памет) беше $980 за 1400×1050 матрица и $1100 с 1600×1200 матрица (нови, IPS, по думи на продавача).

Хората си имат и форум, в който има и инструкции за сглобяване (google translate е ваш добър приятел за тия страници). При мен сглобяването се забави, понеже се оказа, че има вариант на T60p, който е с 16:10 матрица, за който дъното не става, и аз съм взел точно такъв, та си поръчвах нов и чаках да пристигне.

Последва сглабянето с помощта на добрите хора от adsys (на които им отрових живота, щото се оказа доста пипкава работа):
– има малко рязане по кутията (има го описано във форума, със снимки);
– болтовете за закачане са по-малко, дупките на някои са запушени;
– на дъното до конектора за монитор има превключвател за типа на дисплея (1024×768 или по-голям);
– трябва ви DDR4 памет;
– най-вероятно wifi картата от преди няма да ви върши работа, аз си взех моята от T420-ката, и малко трябваше да се лепне с тиксо, понеже е половината слот и нямам преходник;
– CD-то от T60 няма да влезе, понеже е PATA, а конектора на дъното е SATA (не, че ползвам CD). Трябва да си измисля нещо за запушване на дупката;

Неща за дооправяне:
– поне за момента под linux GPU-то не работи (забива на boot), и за това си ползвам xfwm4 вместо compiz, submit-нал съм bug report;
– горните бутони на touchpad-а спират да работят след suspend/resume, направил съм един fix, ама трябва да събера желание да рестартирам.

Моята работна среда на 4:3 се усеща доста по-приятно и най-накрая мога да си пусна email клиента в режим като преди (отляво списък папки, отдясно разделено на две – отгоре списък писма, отдолу отвореното писмо, вместо три вертикални колони, дето едвам пасваха). Също така с тоя процесор вече firefox-а се движи почти прилично, като си оправя и GPU-то, вероятно всичко ще лети.

2018-03-09 чл. 13

March 9th, 2018 by Vasil Kolev

(Даже вестниците вече писаха за това, крайно време е да довърша тоя post)

Та, има “нова” директива на ЕК, за унифициране на законите за авторските права из европейския съюз.
(нова – на 10на години е, ама чак сега се опитва да влезе, и има някакви бегли шансове да се вкара по времето на нашето председателство)

В нея има количество глупости, но частта, с която имам занимание е чл. 13, който в общи линии казва “internet/hosting доставчиците са отговорни за съдържанието, което се качва при тях и трябва да го филтрират”. Не знам за вас, но за мен цялото нещо е твърде много deja vu, в последните 20на години все някой се опитва да пробута нещо подобно, в някакъв вид. Това без значение, че няма как да се направи работеща система, без значение, че всъщност е инфраструктура за цензура (но по-мощна от тази, която има в закона за хазарта) и че в крайна сметка няма да доведе до подобрение за когото и да е, освен може би хората, които продават “филтриращи” системи.

Един от хубавите доводи за това, че такава система няма как да стане идва от wikipedia – има две правителства, които са им поискали да филтрират съдържание, Иран и Китай. Не знам за колко са добре технологично иранците, но ми се струва, че китайците ако можеха, вече щяха да са направили работеща такава система и нямаше да занимават wikipedia. Нещата, които аз съм виждал, са неефективни и с достатъчно грешки (и false positives, и false negatives), че да са неизползваеми за среден размер сайт, понеже така и така някой трябва да преглежда проблемите.
(Вероятно не съм единствения, дето поради идиотщини на youtube постоянно има отворени няколко copyright dispute-а. В един момент човек решава да си ползва собствен streaming и да не го занимават с такива неща…)

Та, има една инициатива по темата това да не влезе, article13.bg, към които съм се включил. Подкрепя ги фондация “Отворени проекти”, initLab и всякакви други добри хора. Вижте, подкрепете, споменете на всички, дето ще ги засегне (всички хостинги, например…).

2018-02-08 FOSDEM

February 8th, 2018 by Vasil Kolev

Спимисе.

В понеделник сутрин се прибрахме от FOSDEM 2018, където правихме видео. Нямам много структурирани спомени, та разни бележки на едно място:

ULB (университетът, в който е FOSDEM) са страшна работа със сигурността, няколко пъти ни заключваха в зали/сгради. И понеже там като цяло хората говорят всякакъв език, стига да е френски, постоянно трябваше да звъним на локалните хора от екипа да се обаждат на охраната да ни отключват. Интересно дали можем да се доберем до тяхната система за контрол…

По време на setup-а се оказа, че имаме един juniper switch за видео laptop-ите. Докато седяхме в NOC-а и си говорехме, че трябва да се конфигурира, влезе един доброволец и каза “аз съм за видео екипа, казаха, че има нещо за кримпване” “можеш ли да конфигурираш juniper switch-ове?” “ами да, занимавал съм се”, след което го затворихме в сървърното и успя да излезе от там чак вечерта…

В първия половин час на конференцията някой се обади по irc – “абе, защо там пише 2017 в ъгъла?”. Оказа се, че фонът е приготвен и commit-нат, но не е бил налян на voctop-ите, та имаше едно много бързо pscp. Във финалната лекция това го споменаха, а преди това няколко човека обикаляха с няколко листа и предложения как да го коригираме (например да напишем 2017++ …).

За 20 минути успяхме в една от залите да сглобим setup, с който лектор да изнесе лекция remote, но па той не можа да се свърже. Жалко, щеше да е интересен експеримент.

Времето в Брюксел беше отвратително – вятър, дъжд, и точно следобяда слънце, че да ми пече в монитора.

Игнат за малко беше на FOSDEM и даже му показах сървърното. Ако го бях пуснал да полази там, дали щеше да спре всичко в рамките на 5 минути…

Като цяло проблемите от нашата техника бяха малко, от тая на университета – доста (аз дебъгвах setup-а в една зала и още не мога да си обясня как е работел досега), но най-големия проблем си остава, че хората не си включват микрофоните… Може би трябва за някакви такива случаи да помислим за някаква система, която чете по движенията на устните и прави субтитри. За догодина задължително monitoring на аудионивата на stream-овете.

Трябва да си намерим полет на връщане, за който да не трябва да ставаме в 6:30, не е човешко.

И понеже все ме питат дали съм гледал една или друга лекция – може би съм един от малкото хора, дето хем са били там, хем не са гледали абсолютно нищо :)

2018-01-28 чукове

January 28th, 2018 by Vasil Kolev

“Не го насилвай, вземи по-голям чук”

Каня се от много време да направя debugging workshop, и около мисленето как точно да стане днес стигнах до интересен извод за инструментите, дето ползвам и си правя за дебъгващи цели и като цяло за разни мои начини на работа.

Чукът е хубаво нещо. Какъвто и проблем да имаш, след удара с чука резултатът има същия вид (сплескан) и донякъде ми се вижда като хубава метафора за начина, по който оправям някакви проблеми. Той може да се опише като “най-краткия и прост начин за достигане на нужното крайно състояние, без да има особено значение какво е началното.

Като за пример, тия дни ми се налагаше да подменя едно парче софтуер в 50-тина клъстера, като всеки от тях имаше м/у 3 и 50 машини. Понеже инструментите, които имам са pssh и pscp, се оказа най-лесно на един пас да копирам нужните файлове по всички сървъри, и на втори пас да се логне pssh и ако трябва, да копира където трябва, иначе просто да изтрие това, което бях копирал. Някакъв по-подреден начин би било да извадя списък на всички машини, на които има нужда да се направи действието и да го направя само там, но щях да го напиша и направя по-бавно, отколкото по грубия и бърз начин.

По подобен начин за друг инструмент си бях написал скрипт, който го налива в цял клъстер и отделен, който го update-ва. В един момент осъзнах, че това е тъпо и направих инсталатора така, че да не му пука, ако има вече нещо инсталирано и просто спокойно да може да слага отгоре (както и ако го прекъсна и го пусна пак, да свърши пак нужната работа). Крайният резултат беше, че общото количество код намаля.

Принципът изглежда да може да се приложи към любимите ми начини за дебъгване – това, което ползвай най-често е strace, което спокойно може да се опише като един от най-тежките чукове за дебъгване. Почти без значение какво дебъгвам – компилиран C код, php, python, perl, java – успявам да видя симптомите и да се ориентирам какво става, въпреки че като цяло за всеки от тия езици има специализиран и вероятно доста по-нежен вариант да се гледа какво става.
(искам да отбележа, че има и други тежки случаи – имам колега, който за да смята някакви математически изрази от време на време вместо да си пусне някакъв калкулатор като bc, пуска gdb и прави в него нещо като “p 1024*1024*231/1.1”)

Замислил се бях дали това всъщност не е погрешно и че трябва да се избягва, и стигнах до извода, че не виждам друг работещ начин. Много често ни се налага да дебъгваме чужд код (който сме link-нали/който е под нас някъде/от който зависим, или просто това са ни изсипали) и вариантът да го прочетем и разберем не е опция, понеже в наши дни почти няма проекти, които да могат да бъдат изчетени и опознати за под седмица-две (рекордно малкият код, който в една от фирмите, в които съм работил и търкаляше основните услуги беше около 20000 реда, което е горе-долу в човешките възможности, и пак ще отнеме доста време да се разгледа, а фирмата в това отношение беше сериозно изключение). Това води до нуждата за всякакви помощни средства, за да можем да се справим, понеже човешката глава има сериозни ограничения по темата, и тук на помощ ни идват чуковете, с които всеки проблем може да бъде сведен до пирон (или хлебарка, която трябва да се прасне достатъчно силно).

(да не говорим, че хората искат да пишат умно, и колкото по-умно пишат, толкова по-трудно се дебъгва това, което са сътворили)