Сообщение Re[15]: WA: 3 млн tcp соединений на одном сервере от 08.08.2020 6:40
Изменено 08.08.2020 7:03 netch80
Re[15]: WA: 3 млн tcp соединений на одном сервере
Здравствуйте, SkyDance, Вы писали:
N>>Потому что туда поступили сообщения из внутренней обработки, и их надо отправить. Терять нельзя.
SD>Напоминает проблему reliable exactly once delivery в distributed environment, а также все ту же handling overload статью.
SD>Что значит "терять нельзя"? В случае перегрузки либо load shedding, либо backpressure, третьего не дано (или по крайней мере мне неизвестно).
Да, тут лучше уточнить. В случае нормальной перегрузки и нормальной реакции на неё — да, управление потоком через backpressure, и его, естественно, делали. В том и дело, что проблема в случае ненормальной реакции из-за свойств рантайма, когда любая реакция на перегрузку в точке её детекта будет заведомо опоздавшей. Это вы в данной дискуссии систематически игнорируете, заменяя абстрактными словами, которые сводятся к "у нас тут своя атмосфера" и "учиться, учиться и учиться", а также "вложите 100500 человеко-часов, это пойдёт всем на пользу" (на самом деле ещё фиг его знает, и очень вероятно, что нет).
N>>Роскошно. Повторю вопрос: как вы будете решать иначе?
SD>Я уже объяснил. Можно было написать NIF-based socket,
Объём работы. Проблема поддержки этого всего.
Вы, мысля масштаба WhatsApp и Facebook, не понимаете, что при маленькой команде (а она никогда не выходила за 10 человек, из которых собственно проблемы транспорта понимали максимум 3, а ведь есть и другие задачи) постановка в духе "написать NIF-based socket" означает серьёзное увеличение затрат на разработку, поддержку и сопровождение; кроме того, она означает, что инструмент не справился с теми задачами, для которых он и был, согласно рассказам авторов, рассчитан.
Так как сочетание NIF-сокета с остальным Erlang означает, кроме того, что этот сокет должен кто-то написать и поддерживать (ладно, я бы написал, у меня "экспертизы" хватает, а дальше кто этим займётся?), ещё и отладку бутерброда и Erlang+C (а то, что отладка будет нужна — к гадалке не ходи). Это проблема всех бутербродных построений, и выигрывают те средства, в которых они не нужны как минимум для 99% базовой функциональности (вспоминаем Java, C#, Python...)
SD> можно было реализовать backpressure, можно было разобраться и поднастроить GC. Можно было подумать про архитектуру и ее огранчиения: почему у вас строго одна выходная "труба"? Ведь это single point of failure. Это по определению ненадежно.
Да вы уже, похоже, цепляетесь за всё подряд. Во втором проекте (я тут везде его так называл, пусть остаётся) единственность выходной трубы это условия вписывания во внешние условия задачи, их менять нельзя. Но даже если бы этого не было, абсолютно не вопрос надёжности, одна тут "труба" или нет: в случае любой потери надо перепосылать с точки обрыва, выкапывая первое недоставленное сообщение. И общей проблемой, наверно, 99% взаимодействий является то, что если сообщение потеряно, то его надо перепосылать. Множественные "трубы" тут ничего не дадут.
N>> Проблема в receive именно в количестве сообщений, а если я все передаваемые пакеты набью в одно, оно будет тяжёлым (объём данных никуда не девается), но одним. Потом процесс будет разбирать его и отправлять по кусочкам.
SD>На этом месте следовало бы остановиться и посмотреть, как именно сделан message passing для тяжелых сообщений (точнее, binaries внутри). Само сообщение будет крошечным, и будет содержать только ref-counter pointer на binary (который лежит вообще в отдельном аллокаторе).
Ага, то есть хорошо было бы ещё и в binary их перепаковывать, причём размера не меньше сколько-то (с какого размера там включается оптимизация)? И это на внутреннем взаимодействии (внутри одной ноды)? Вам не кажется, что это слишком большие требования для простой задачи?
SD>Позвольте мне пропустить дальнейшее описание рукопашного flow control protocol, ибо сама необходимость водружать столь сложный механизм была основна на заведомо неверном предположении. Ох неспроста "The Art of Challenging Assumptions" по статистике Whova App набрал максимум посещаемости из всех выступлений на той конференции
Я посмотрел, спасибо. Великолепный английский как для родного русского — завидую... остальное — или подробности Erlang-решений, или вода. Любой грамотный разработчик с опытом скажет то же самое, пусть и не столь красиво, но во всём этом нет ответа на главное: мы в принципе все свои действия основываем на предположениях на основании существующего опыта, и их подавляющее большинство на самом деле ничему не мешает, и менять их нереально и бессмысленно; но, после того, как надо просто выделить ресурсы на измерения и проверки того, что казалось очевидным (а ресурсы тут важны всегда, и сурово критичны — почти всегда) — остаётся определить, с чего именно начинать измерения и проверки, а это получается только с опытом в теме.
В сумме, доклад — красивый пиар вас лично и фирмы. Полагаю, что вы таки достигли поставленных для этого пиара целей.
И чем же тут было "заведомо неверное" предположение — вы не объяснили, так что ответа нет. Ответ в духе "вы предположили, что можно обойтись первыми приходящими в голову средствами и не подумать вызвать X, Y, Z, чёрта лысого, подключить библиотеку WWW243972" это не ответ, это издевательство.
N>>Вот это как раз тот случай, когда мы применили ETS, что вам так не нравится.
SD>Ну, в моей практике встречались и еще более странные применения ETS. Равно как и еще более необычные side channels — скажем, вместо ETS можно было бы завести отдельный процесс (условно назовем его locker) который бы отвечал на вопрос "уже можно или еще нет".
Дададад. "Отвечал". То есть шло бы обратно сообщение, которое вставало бы в конец очереди. Вы это серьёзно?
SD> Способов организвать flow control — не счесть. И не только на Эрланге, на Java/C++/Python есть ничуть не меньше разных забавных конструкций на эту тему. Скажем, проверять глобальные флажки. А что, отличный вопрос для собеседования — поговорить о семантике volatile переменных
И вы тут то ли намеренно ёрничаете, то ли явно не в курсе про volatile в многонитевом окружении?
SD>В общем, можно построить очень много ненужных велосипедов, если с самого начала не разобраться в задаче как следует.
SD>Но. Хочу отдать должное, даже в самых простых решениях вроде этого самого binary buffer можно очень легко сделать ошибку. Вот пример. Прям один-в-один ваш случай, включая gen_tcp:send, и middleman-process. Неспроста я про TLS и ужасы говорил
Перескажите словами, что происходит, если хотите понимания. Я-то ещё разберу происходящее за пару часов (TLS на Erlang никогда не заводил), но если кто ещё читает — то точно не поймёт.
N>>Теперь: у этой системы есть свой мониторинг мониторинга.
SD>Каждый такой пример "системы мониторинга" зачастую следствие неумения организовать supervision tree.
Как вы через супервизоры будете снимать набор количественных показателей работы приложения (текущие уровни, счётчики, и всё такое)? Или вы под словом "мониторинг" тут понимаете только
>> какие ошибки считать transient, какие permanent, и на каком периоде времени/частоте
?
Ню ню (tm)
Или опять ETS, который вы хотели бы устранить?
SD>Можно обсудить эти вопросы более предметно, но уж шибко много печатать придется. В общем и целом, я всегда буду задавать один и тот же вопрос — "для чего", и "какие действия принимаются на основе полученных фактов".
Я думаю, последние 20 лет вопрос "какие действия принимаются..." на основе счётчиков событий (банально, полученных/посланных сообщений по выбранной классификации, и всё такое) — просто не должен озвучиваться. Вопрос должен быть — делаются ли эти действия вообще и правильные ли они.
N>>Просто: пусть у нас 3 очереди
SD>Нет, это не просто. Это как раз сложно. Просто — это когда у вас 3 процесса, и у каждого своя очередь.
Докажите. Я объяснил, что именно достигается несколькими очередями и как оно стабилизирует работу системы. Теперь хочу послушать, каким образом вы этими 3 раздельными процессами добьётесь аналогичного результата. На всякий случай приведу ещё раз базовые условия, хотя бы в таком наборе:
1) Речь идёт об управлении конкретным процессом и его контроле через единственный, по вашим же правилам, разрешённый интерфейс в виде асинхронно поступающих сообщений. Канал через ETS не разрешаем.
2) Надо управлять этим процессом, включая регулирование параметров (оперативное! ждать секунды, пока он прочтёт сообщение, нельзя).
3) Увы, gen_call иногда (а может, и всегда) нужен из-за того, что на нём работает некоторый интерфейс, который мы менять не можем.
Если вы скажете, что надо заставлять источники группировать сообщения в большие бинарные пакеты, как выше — то это не подходит потому, что в этом случае тем более нафиг не нужно 3 процесса.
N>>И, что крайне важно, это всё не помешало бы всему остальному, что вы тут пишете про "правильный дизайн".
SD>Думаю, что ваша точка зрения не имеет достаточной научной базы.
О том, что не помешало бы? То есть у вас есть очень себе "challenging" предположение о том, что чем-то помешало бы? Хочу послушать предпосылки к этому предположению, это становится интересным
А также научную базу, как от этого попортилось бы что-то 
SD>Понимаю, что у вас есть вполне конкретный случай, конкретная проблема, но вот что неверно в ваших рассуждениях, вы пытаетесь решить ее не на том уровне. Не скажу что вы одиноки в таком подходе
Уж сколько в моей практике приходилось рубить патчей на тему "вот у нас там проблема, но разбиратсья нам лень, но мы видимо что тремя уровнями ниже можно сделать подпорку, и плевать, что этой подпоркой мы испортим стройную модель".
И снова от вас никакой конкретики, только "стройная модель" (стройность которой на самом деле ничуть не испортит введение нескольких очередей) и ссылка на какие-то посторонние случаи, про которые ничего не известно.
N>>И от синхронных вызовов отказаться как-то нереально. Хотя сократить их долю, да, можно.
SD>Представляете, что было бы, если бы ваш мозг мог синхронно зависнуть, ожидая ack'а от сердечной мышцы?
Плохая аналогия подобна котёнку с дверцей (tm).
Ваша аналогия с организмом, повторюсь, неадекватна хотя бы потому, что в сердце и мозгу нет "процессов", аналогичных Erlangʼовым, там всё иначе. Но если вы уж начали обсуждать такими аналогиями, почитайте про основные проблемы анестезии и почему является проблемой, что регулировка дыхания мозгом неадекватна и в какой-то момент начинает работать против своей цели.
N>>Вот именно, что ищет решения. Нашим решением для второго проекта был уход от Erlang. И не пожалели.
SD>Очень разумный подход.
Если задача в том, чтобы "быстрее сделать", выбирать надо тот инструмент, в котором разбираешься лучше всего.
А почти все задачи начинаются с "быстрее сделать", построить MVP или какой там будет его аналог. И вот тут становится критичным, что люди, которые не разбираются во всех тонкостях, должны поднять и сопровождать проект на начальной стадии.
Про кривую обучения вы сами вспомнили. И именно ваша совсем не пологая кривая (за пределами задач из песочницы) тут очень мешает, что в итоге уходят на другой инструмент. И вы отказываетесь применить простые средства избавления от этой крутизны, ссылаясь на "отсутствие научной базы" (которую сами же и отказываетесь формировать).
И вот это принципиальная разница в моей позиции после первого и после второго проекта: если первое я ещё списывал на недоработки инструмента, то второе уже твёрдая позиция команды разработчиков — а, значит, ничего хорошего ожидать от инструмента далее не приходится.
SD>Вы описали один из test cases для off-heap message queue. В 2010 ее не было (появилась в OTP 19, то есть 5 лет назад).
То есть пробовать не хотите? Спасибо, Ч.Т.Д.
SD>К делу не относится, но здесь мне хочется поддержать Pzz,
SD>
Ну поддерживайте себе дальше. Мне уже скорее пофиг, я понял, что от Erlang тут толку не добиться, по крайней мере, пока основной состав разработчиков и управляющих (как бы вы их ни называли) не сменится. Но это, увы, типовая судьба любых проектов — их доводят до ума самые упёртые, а не отличающиеся широтой и глубиной взглядов.
А для практического применения я буду выбирать средства с максимально пологой кривой обучения (будем уже держаться в рамках этого термина), потому что полученные продукты будут выживать даже при полной смене команды.
Ну и у меня, по своему опыту, далеко не такие мрачные выводы о качестве людей (по крайней мере программистов). Может, потому, что я никогда не работал в аутсорсе.
SD>Это ваши личные домыслы. Я лишь утверждаю, что вне зависимости от языка или парадигмы, разработчик, который в состоянии challenge assumptions, который может провести root cause analysis, а затем решить проблему, на порядок более производителен "среднего качества разработчика".
Ну вот пока вы считаете "средним качеством" тех, кто не может и даже не хочет... ok, ваше дело.
N>>Потому что туда поступили сообщения из внутренней обработки, и их надо отправить. Терять нельзя.
SD>Напоминает проблему reliable exactly once delivery в distributed environment, а также все ту же handling overload статью.
SD>Что значит "терять нельзя"? В случае перегрузки либо load shedding, либо backpressure, третьего не дано (или по крайней мере мне неизвестно).
Да, тут лучше уточнить. В случае нормальной перегрузки и нормальной реакции на неё — да, управление потоком через backpressure, и его, естественно, делали. В том и дело, что проблема в случае ненормальной реакции из-за свойств рантайма, когда любая реакция на перегрузку в точке её детекта будет заведомо опоздавшей. Это вы в данной дискуссии систематически игнорируете, заменяя абстрактными словами, которые сводятся к "у нас тут своя атмосфера" и "учиться, учиться и учиться", а также "вложите 100500 человеко-часов, это пойдёт всем на пользу" (на самом деле ещё фиг его знает, и очень вероятно, что нет).
N>>Роскошно. Повторю вопрос: как вы будете решать иначе?
SD>Я уже объяснил. Можно было написать NIF-based socket,
Объём работы. Проблема поддержки этого всего.
Вы, мысля масштаба WhatsApp и Facebook, не понимаете, что при маленькой команде (а она никогда не выходила за 10 человек, из которых собственно проблемы транспорта понимали максимум 3, а ведь есть и другие задачи) постановка в духе "написать NIF-based socket" означает серьёзное увеличение затрат на разработку, поддержку и сопровождение; кроме того, она означает, что инструмент не справился с теми задачами, для которых он и был, согласно рассказам авторов, рассчитан.
Так как сочетание NIF-сокета с остальным Erlang означает, кроме того, что этот сокет должен кто-то написать и поддерживать (ладно, я бы написал, у меня "экспертизы" хватает, а дальше кто этим займётся?), ещё и отладку бутерброда и Erlang+C (а то, что отладка будет нужна — к гадалке не ходи). Это проблема всех бутербродных построений, и выигрывают те средства, в которых они не нужны как минимум для 99% базовой функциональности (вспоминаем Java, C#, Python...)
SD> можно было реализовать backpressure, можно было разобраться и поднастроить GC. Можно было подумать про архитектуру и ее огранчиения: почему у вас строго одна выходная "труба"? Ведь это single point of failure. Это по определению ненадежно.
Да вы уже, похоже, цепляетесь за всё подряд. Во втором проекте (я тут везде его так называл, пусть остаётся) единственность выходной трубы это условия вписывания во внешние условия задачи, их менять нельзя. Но даже если бы этого не было, абсолютно не вопрос надёжности, одна тут "труба" или нет: в случае любой потери надо перепосылать с точки обрыва, выкапывая первое недоставленное сообщение. И общей проблемой, наверно, 99% взаимодействий является то, что если сообщение потеряно, то его надо перепосылать. Множественные "трубы" тут ничего не дадут.
N>> Проблема в receive именно в количестве сообщений, а если я все передаваемые пакеты набью в одно, оно будет тяжёлым (объём данных никуда не девается), но одним. Потом процесс будет разбирать его и отправлять по кусочкам.
SD>На этом месте следовало бы остановиться и посмотреть, как именно сделан message passing для тяжелых сообщений (точнее, binaries внутри). Само сообщение будет крошечным, и будет содержать только ref-counter pointer на binary (который лежит вообще в отдельном аллокаторе).
Ага, то есть хорошо было бы ещё и в binary их перепаковывать, причём размера не меньше сколько-то (с какого размера там включается оптимизация)? И это на внутреннем взаимодействии (внутри одной ноды)? Вам не кажется, что это слишком большие требования для простой задачи?
SD>Позвольте мне пропустить дальнейшее описание рукопашного flow control protocol, ибо сама необходимость водружать столь сложный механизм была основна на заведомо неверном предположении. Ох неспроста "The Art of Challenging Assumptions" по статистике Whova App набрал максимум посещаемости из всех выступлений на той конференции
Я посмотрел, спасибо. Великолепный английский как для родного русского — завидую... остальное — или подробности Erlang-решений, или вода. Любой грамотный разработчик с опытом скажет то же самое, пусть и не столь красиво, но во всём этом нет ответа на главное: мы в принципе все свои действия основываем на предположениях на основании существующего опыта, и их подавляющее большинство на самом деле ничему не мешает, и менять их нереально и бессмысленно; но, после того, как надо просто выделить ресурсы на измерения и проверки того, что казалось очевидным (а ресурсы тут важны всегда, и сурово критичны — почти всегда) — остаётся определить, с чего именно начинать измерения и проверки, а это получается только с опытом в теме.
В сумме, доклад — красивый пиар вас лично и фирмы. Полагаю, что вы таки достигли поставленных для этого пиара целей.
И чем же тут было "заведомо неверное" предположение — вы не объяснили, так что ответа нет. Ответ в духе "вы предположили, что можно обойтись первыми приходящими в голову средствами и не подумать вызвать X, Y, Z, чёрта лысого, подключить библиотеку WWW243972" это не ответ, это издевательство.
N>>Вот это как раз тот случай, когда мы применили ETS, что вам так не нравится.
SD>Ну, в моей практике встречались и еще более странные применения ETS. Равно как и еще более необычные side channels — скажем, вместо ETS можно было бы завести отдельный процесс (условно назовем его locker) который бы отвечал на вопрос "уже можно или еще нет".
Дададад. "Отвечал". То есть шло бы обратно сообщение, которое вставало бы в конец очереди. Вы это серьёзно?
SD> Способов организвать flow control — не счесть. И не только на Эрланге, на Java/C++/Python есть ничуть не меньше разных забавных конструкций на эту тему. Скажем, проверять глобальные флажки. А что, отличный вопрос для собеседования — поговорить о семантике volatile переменных
И вы тут то ли намеренно ёрничаете, то ли явно не в курсе про volatile в многонитевом окружении?
SD>В общем, можно построить очень много ненужных велосипедов, если с самого начала не разобраться в задаче как следует.
SD>Но. Хочу отдать должное, даже в самых простых решениях вроде этого самого binary buffer можно очень легко сделать ошибку. Вот пример. Прям один-в-один ваш случай, включая gen_tcp:send, и middleman-process. Неспроста я про TLS и ужасы говорил
Перескажите словами, что происходит, если хотите понимания. Я-то ещё разберу происходящее за пару часов (TLS на Erlang никогда не заводил), но если кто ещё читает — то точно не поймёт.
N>>Теперь: у этой системы есть свой мониторинг мониторинга.
SD>Каждый такой пример "системы мониторинга" зачастую следствие неумения организовать supervision tree.
Как вы через супервизоры будете снимать набор количественных показателей работы приложения (текущие уровни, счётчики, и всё такое)? Или вы под словом "мониторинг" тут понимаете только
>> какие ошибки считать transient, какие permanent, и на каком периоде времени/частоте
?
Ню ню (tm)
Или опять ETS, который вы хотели бы устранить?
SD>Можно обсудить эти вопросы более предметно, но уж шибко много печатать придется. В общем и целом, я всегда буду задавать один и тот же вопрос — "для чего", и "какие действия принимаются на основе полученных фактов".
Я думаю, последние 20 лет вопрос "какие действия принимаются..." на основе счётчиков событий (банально, полученных/посланных сообщений по выбранной классификации, и всё такое) — просто не должен озвучиваться. Вопрос должен быть — делаются ли эти действия вообще и правильные ли они.
N>>Просто: пусть у нас 3 очереди
SD>Нет, это не просто. Это как раз сложно. Просто — это когда у вас 3 процесса, и у каждого своя очередь.
Докажите. Я объяснил, что именно достигается несколькими очередями и как оно стабилизирует работу системы. Теперь хочу послушать, каким образом вы этими 3 раздельными процессами добьётесь аналогичного результата. На всякий случай приведу ещё раз базовые условия, хотя бы в таком наборе:
1) Речь идёт об управлении конкретным процессом и его контроле через единственный, по вашим же правилам, разрешённый интерфейс в виде асинхронно поступающих сообщений. Канал через ETS не разрешаем.
2) Надо управлять этим процессом, включая регулирование параметров (оперативное! ждать секунды, пока он прочтёт сообщение, нельзя).
3) Увы, gen_call иногда (а может, и всегда) нужен из-за того, что на нём работает некоторый интерфейс, который мы менять не можем.
Если вы скажете, что надо заставлять источники группировать сообщения в большие бинарные пакеты, как выше — то это не подходит потому, что в этом случае тем более нафиг не нужно 3 процесса.
N>>И, что крайне важно, это всё не помешало бы всему остальному, что вы тут пишете про "правильный дизайн".
SD>Думаю, что ваша точка зрения не имеет достаточной научной базы.
О том, что не помешало бы? То есть у вас есть очень себе "challenging" предположение о том, что чем-то помешало бы? Хочу послушать предпосылки к этому предположению, это становится интересным
SD>Понимаю, что у вас есть вполне конкретный случай, конкретная проблема, но вот что неверно в ваших рассуждениях, вы пытаетесь решить ее не на том уровне. Не скажу что вы одиноки в таком подходе
И снова от вас никакой конкретики, только "стройная модель" (стройность которой на самом деле ничуть не испортит введение нескольких очередей) и ссылка на какие-то посторонние случаи, про которые ничего не известно.
N>>И от синхронных вызовов отказаться как-то нереально. Хотя сократить их долю, да, можно.
SD>Представляете, что было бы, если бы ваш мозг мог синхронно зависнуть, ожидая ack'а от сердечной мышцы?
Плохая аналогия подобна котёнку с дверцей (tm).
Ваша аналогия с организмом, повторюсь, неадекватна хотя бы потому, что в сердце и мозгу нет "процессов", аналогичных Erlangʼовым, там всё иначе. Но если вы уж начали обсуждать такими аналогиями, почитайте про основные проблемы анестезии и почему является проблемой, что регулировка дыхания мозгом неадекватна и в какой-то момент начинает работать против своей цели.
N>>Вот именно, что ищет решения. Нашим решением для второго проекта был уход от Erlang. И не пожалели.
SD>Очень разумный подход.
Если задача в том, чтобы "быстрее сделать", выбирать надо тот инструмент, в котором разбираешься лучше всего.
А почти все задачи начинаются с "быстрее сделать", построить MVP или какой там будет его аналог. И вот тут становится критичным, что люди, которые не разбираются во всех тонкостях, должны поднять и сопровождать проект на начальной стадии.
Про кривую обучения вы сами вспомнили. И именно ваша совсем не пологая кривая (за пределами задач из песочницы) тут очень мешает, что в итоге уходят на другой инструмент. И вы отказываетесь применить простые средства избавления от этой крутизны, ссылаясь на "отсутствие научной базы" (которую сами же и отказываетесь формировать).
И вот это принципиальная разница в моей позиции после первого и после второго проекта: если первое я ещё списывал на недоработки инструмента, то второе уже твёрдая позиция команды разработчиков — а, значит, ничего хорошего ожидать от инструмента далее не приходится.
SD>Вы описали один из test cases для off-heap message queue. В 2010 ее не было (появилась в OTP 19, то есть 5 лет назад).
То есть пробовать не хотите? Спасибо, Ч.Т.Д.
SD>К делу не относится, но здесь мне хочется поддержать Pzz,
SD>
SD>Вот это — чертова проблема. Я отношусь к тем людям, которые впятером могут гору свернуть, а потом аккуратно поставить ее на место, а жить приходится в мире, рассчитанном на массовое трудоустройство альтернативно умственно одаренных людей.
Ну поддерживайте себе дальше. Мне уже скорее пофиг, я понял, что от Erlang тут толку не добиться, по крайней мере, пока основной состав разработчиков и управляющих (как бы вы их ни называли) не сменится. Но это, увы, типовая судьба любых проектов — их доводят до ума самые упёртые, а не отличающиеся широтой и глубиной взглядов.
А для практического применения я буду выбирать средства с максимально пологой кривой обучения (будем уже держаться в рамках этого термина), потому что полученные продукты будут выживать даже при полной смене команды.
Ну и у меня, по своему опыту, далеко не такие мрачные выводы о качестве людей (по крайней мере программистов). Может, потому, что я никогда не работал в аутсорсе.
SD>Это ваши личные домыслы. Я лишь утверждаю, что вне зависимости от языка или парадигмы, разработчик, который в состоянии challenge assumptions, который может провести root cause analysis, а затем решить проблему, на порядок более производителен "среднего качества разработчика".
Ну вот пока вы считаете "средним качеством" тех, кто не может и даже не хочет... ok, ваше дело.
Re[15]: WA: 3 млн tcp соединений на одном сервере
Здравствуйте, SkyDance, Вы писали:
N>>Потому что туда поступили сообщения из внутренней обработки, и их надо отправить. Терять нельзя.
SD>Напоминает проблему reliable exactly once delivery в distributed environment, а также все ту же handling overload статью.
SD>Что значит "терять нельзя"? В случае перегрузки либо load shedding, либо backpressure, третьего не дано (или по крайней мере мне неизвестно).
Да, тут лучше уточнить. В случае нормальной перегрузки и нормальной реакции на неё — да, управление потоком через backpressure, и его, естественно, делали. В том и дело, что проблема в случае ненормальной реакции из-за свойств рантайма, когда любая реакция на перегрузку в точке её детекта будет заведомо опоздавшей. Это вы в данной дискуссии систематически игнорируете, заменяя абстрактными словами, которые сводятся к "у нас тут своя атмосфера" и "учиться, учиться и учиться", а также "вложите 100500 человеко-часов, это пойдёт всем на пользу" (на самом деле ещё фиг его знает, и очень вероятно, что нет).
N>>Роскошно. Повторю вопрос: как вы будете решать иначе?
SD>Я уже объяснил. Можно было написать NIF-based socket,
Объём работы. Проблема поддержки этого всего.
Вы, мысля масштаба WhatsApp и Facebook, не понимаете, что при маленькой команде (а она никогда не выходила за 10 человек, из которых собственно проблемы транспорта понимали максимум 3, а ведь есть и другие задачи) постановка в духе "написать NIF-based socket" означает серьёзное увеличение затрат на разработку, поддержку и сопровождение; кроме того, она означает, что инструмент не справился с теми задачами, для которых он и был, согласно рассказам авторов, рассчитан.
Так как сочетание NIF-сокета с остальным Erlang означает, кроме того, что этот сокет должен кто-то написать и поддерживать (ладно, я бы написал, у меня "экспертизы" хватает, а дальше кто этим займётся?), ещё и отладку бутерброда и Erlang+C (а то, что отладка будет нужна — к гадалке не ходи). Это проблема всех бутербродных построений, и выигрывают те средства, в которых они не нужны как минимум для 99% базовой функциональности (вспоминаем Java, C#, Python...)
SD> можно было реализовать backpressure, можно было разобраться и поднастроить GC. Можно было подумать про архитектуру и ее огранчиения: почему у вас строго одна выходная "труба"? Ведь это single point of failure. Это по определению ненадежно.
Да вы уже, похоже, цепляетесь за всё подряд. Во втором проекте (я тут везде его так называл, пусть остаётся) единственность выходной трубы это условия вписывания во внешние условия задачи, их менять нельзя. Но даже если бы этого не было, абсолютно не вопрос надёжности, одна тут "труба" или нет: в случае любой потери надо перепосылать с точки обрыва, выкапывая первое недоставленное сообщение. И общей проблемой, наверно, 99% взаимодействий является то, что если сообщение потеряно, то его надо перепосылать. Множественные "трубы" тут ничего не дадут.
N>> Проблема в receive именно в количестве сообщений, а если я все передаваемые пакеты набью в одно, оно будет тяжёлым (объём данных никуда не девается), но одним. Потом процесс будет разбирать его и отправлять по кусочкам.
SD>На этом месте следовало бы остановиться и посмотреть, как именно сделан message passing для тяжелых сообщений (точнее, binaries внутри). Само сообщение будет крошечным, и будет содержать только ref-counter pointer на binary (который лежит вообще в отдельном аллокаторе).
Ага, то есть хорошо было бы ещё и в binary их перепаковывать, причём размера не меньше сколько-то (с какого размера там включается оптимизация)? И это на внутреннем взаимодействии (внутри одной ноды)? Вам не кажется, что это слишком большие требования для простой задачи?
SD>Позвольте мне пропустить дальнейшее описание рукопашного flow control protocol, ибо сама необходимость водружать столь сложный механизм была основна на заведомо неверном предположении. Ох неспроста "The Art of Challenging Assumptions" по статистике Whova App набрал максимум посещаемости из всех выступлений на той конференции
Я посмотрел, спасибо. Великолепный английский как для родного русского — завидую... остальное — или подробности Erlang-решений, или вода. Любой грамотный разработчик с опытом скажет то же самое, пусть и не столь красиво, но во всём этом нет ответа на главное: мы в принципе все свои действия основываем на предположениях на основании существующего опыта, и их подавляющее большинство на самом деле ничему не мешает, и менять их нереально и бессмысленно; но, после того, как надо просто выделить ресурсы на измерения и проверки того, что казалось очевидным (а ресурсы тут важны всегда, и сурово критичны — почти всегда) — остаётся определить, с чего именно начинать измерения и проверки, а это получается только с опытом в теме.
В сумме, доклад — красивый пиар вас лично и фирмы. Полагаю, что вы таки достигли поставленных для этого пиара целей.
И чем же тут было "заведомо неверное" предположение — вы не объяснили, так что ответа нет. Ответ в духе "вы предположили, что можно обойтись первыми приходящими в голову средствами и не подумать вызвать X, Y, Z, чёрта лысого, подключить библиотеку WWW243972" это не ответ, это издевательство.
N>>Вот это как раз тот случай, когда мы применили ETS, что вам так не нравится.
SD>Ну, в моей практике встречались и еще более странные применения ETS. Равно как и еще более необычные side channels — скажем, вместо ETS можно было бы завести отдельный процесс (условно назовем его locker) который бы отвечал на вопрос "уже можно или еще нет".
Дададад. "Отвечал". То есть шло бы обратно сообщение, которое вставало бы в конец очереди. Вы это серьёзно?
SD> Способов организвать flow control — не счесть. И не только на Эрланге, на Java/C++/Python есть ничуть не меньше разных забавных конструкций на эту тему. Скажем, проверять глобальные флажки. А что, отличный вопрос для собеседования — поговорить о семантике volatile переменных
И вы тут то ли намеренно ёрничаете, то ли явно не в курсе про volatile в многонитевом окружении?
SD>В общем, можно построить очень много ненужных велосипедов, если с самого начала не разобраться в задаче как следует.
SD>Но. Хочу отдать должное, даже в самых простых решениях вроде этого самого binary buffer можно очень легко сделать ошибку. Вот пример. Прям один-в-один ваш случай, включая gen_tcp:send, и middleman-process. Неспроста я про TLS и ужасы говорил
Перескажите словами, что происходит, если хотите понимания. Я-то ещё разберу происходящее за пару часов (TLS на Erlang никогда не заводил), но если кто ещё читает — то точно не поймёт.
N>>Теперь: у этой системы есть свой мониторинг мониторинга.
SD>Каждый такой пример "системы мониторинга" зачастую следствие неумения организовать supervision tree.
Как вы через супервизоры будете снимать набор количественных показателей работы приложения (текущие уровни, счётчики, и всё такое)? Или вы под словом "мониторинг" тут понимаете только
>> какие ошибки считать transient, какие permanent, и на каком периоде времени/частоте
?
Ню ню (tm)
Или опять ETS, который вы хотели бы устранить?
SD>Можно обсудить эти вопросы более предметно, но уж шибко много печатать придется. В общем и целом, я всегда буду задавать один и тот же вопрос — "для чего", и "какие действия принимаются на основе полученных фактов".
Я думаю, последние 20 лет вопрос "какие действия принимаются..." на основе счётчиков событий (банально, полученных/посланных сообщений по выбранной классификации, и всё такое) — просто не должен озвучиваться. Вопрос должен быть — делаются ли эти действия вообще и правильные ли они.
N>>Просто: пусть у нас 3 очереди
SD>Нет, это не просто. Это как раз сложно. Просто — это когда у вас 3 процесса, и у каждого своя очередь.
Докажите. Я объяснил, что именно достигается несколькими очередями и как оно стабилизирует работу системы. Теперь хочу послушать, каким образом вы этими 3 раздельными процессами добьётесь аналогичного результата. На всякий случай приведу ещё раз базовые условия, хотя бы в таком наборе:
1) Речь идёт об управлении конкретным процессом и его контроле через единственный, по вашим же правилам, разрешённый интерфейс в виде асинхронно поступающих сообщений. Канал через ETS не разрешаем.
2) Надо управлять этим процессом, включая регулирование параметров (оперативное! ждать секунды, пока он прочтёт сообщение, нельзя).
3) Увы, gen_call иногда (а может, и всегда) нужен из-за того, что на нём работает некоторый интерфейс, который мы менять не можем.
Если вы скажете, что надо заставлять источники группировать сообщения в большие бинарные пакеты, как выше — то это не подходит потому, что в этом случае тем более нафиг не нужно 3 процесса.
N>>И, что крайне важно, это всё не помешало бы всему остальному, что вы тут пишете про "правильный дизайн".
SD>Думаю, что ваша точка зрения не имеет достаточной научной базы.
О том, что не помешало бы? То есть у вас есть очень себе "challenging" предположение о том, что чем-то помешало бы? Хочу послушать предпосылки к этому предположению, это становится интересным
А также научную базу, как от этого попортилось бы что-то 
SD>Понимаю, что у вас есть вполне конкретный случай, конкретная проблема, но вот что неверно в ваших рассуждениях, вы пытаетесь решить ее не на том уровне. Не скажу что вы одиноки в таком подходе
Уж сколько в моей практике приходилось рубить патчей на тему "вот у нас там проблема, но разбиратсья нам лень, но мы видимо что тремя уровнями ниже можно сделать подпорку, и плевать, что этой подпоркой мы испортим стройную модель".
И снова от вас никакой конкретики, только "стройная модель" (стройность которой на самом деле ничуть не испортит введение нескольких очередей) и ссылка на какие-то посторонние случаи, про которые ничего не известно.
N>>И от синхронных вызовов отказаться как-то нереально. Хотя сократить их долю, да, можно.
SD>Представляете, что было бы, если бы ваш мозг мог синхронно зависнуть, ожидая ack'а от сердечной мышцы?
Плохая аналогия подобна котёнку с дверцей (tm).
Ваша аналогия с организмом, повторюсь, неадекватна хотя бы потому, что в сердце и мозгу нет "процессов", аналогичных Erlangʼовым, там всё иначе. Но если вы уж начали обсуждать такими аналогиями, почитайте про основные проблемы анестезии и почему является проблемой, что регулировка дыхания мозгом неадекватна и в какой-то момент начинает работать против своей цели.
N>>Вот именно, что ищет решения. Нашим решением для второго проекта был уход от Erlang. И не пожалели.
SD>Очень разумный подход.
Если задача в том, чтобы "быстрее сделать", выбирать надо тот инструмент, в котором разбираешься лучше всего.
А почти все задачи начинаются с "быстрее сделать", построить MVP или какой там будет его аналог. И вот тут становится критичным, что люди, которые не разбираются во всех тонкостях, должны поднять и сопровождать проект на начальной стадии.
Про кривую обучения вы сами вспомнили. И именно ваша совсем не пологая кривая (за пределами задач из песочницы) тут очень мешает, что в итоге уходят на другой инструмент. И вы отказываетесь применить простые средства избавления от этой крутизны, ссылаясь на "отсутствие научной базы" (которую сами же и отказываетесь формировать).
Ну раз вы сами подтвердили твёрдую позицию команды разработчиков и спутников, как вы — значит, ничего хорошего ожидать от инструмента далее не приходится. Остаток сообщения это подтверждает.
SD>Вы описали один из test cases для off-heap message queue. В 2010 ее не было (появилась в OTP 19, то есть 5 лет назад).
То есть пробовать не хотите? Спасибо, Ч.Т.Д.
SD>К делу не относится, но здесь мне хочется поддержать Pzz,
SD>
Ну поддерживайте себе дальше. Мне уже скорее пофиг, я понял, что от Erlang тут толку не добиться, по крайней мере, пока основной состав разработчиков и управляющих (как бы вы их ни называли) не сменится. Но это, увы, типовая судьба любых проектов — их доводят до ума самые упёртые, а не отличающиеся широтой и глубиной взглядов.
А для практического применения я буду выбирать средства с максимально пологой кривой обучения (будем уже держаться в рамках этого термина), потому что полученные продукты будут выживать даже при полной смене команды.
Ну и у меня, по своему опыту, далеко не такие мрачные выводы о качестве людей (по крайней мере программистов). Может, потому, что я никогда не работал в аутсорсе.
SD>Это ваши личные домыслы. Я лишь утверждаю, что вне зависимости от языка или парадигмы, разработчик, который в состоянии challenge assumptions, который может провести root cause analysis, а затем решить проблему, на порядок более производителен "среднего качества разработчика".
Ну вот пока вы считаете "средним качеством" тех, кто не может и даже не хочет... ok, ваше дело.
N>>Потому что туда поступили сообщения из внутренней обработки, и их надо отправить. Терять нельзя.
SD>Напоминает проблему reliable exactly once delivery в distributed environment, а также все ту же handling overload статью.
SD>Что значит "терять нельзя"? В случае перегрузки либо load shedding, либо backpressure, третьего не дано (или по крайней мере мне неизвестно).
Да, тут лучше уточнить. В случае нормальной перегрузки и нормальной реакции на неё — да, управление потоком через backpressure, и его, естественно, делали. В том и дело, что проблема в случае ненормальной реакции из-за свойств рантайма, когда любая реакция на перегрузку в точке её детекта будет заведомо опоздавшей. Это вы в данной дискуссии систематически игнорируете, заменяя абстрактными словами, которые сводятся к "у нас тут своя атмосфера" и "учиться, учиться и учиться", а также "вложите 100500 человеко-часов, это пойдёт всем на пользу" (на самом деле ещё фиг его знает, и очень вероятно, что нет).
N>>Роскошно. Повторю вопрос: как вы будете решать иначе?
SD>Я уже объяснил. Можно было написать NIF-based socket,
Объём работы. Проблема поддержки этого всего.
Вы, мысля масштаба WhatsApp и Facebook, не понимаете, что при маленькой команде (а она никогда не выходила за 10 человек, из которых собственно проблемы транспорта понимали максимум 3, а ведь есть и другие задачи) постановка в духе "написать NIF-based socket" означает серьёзное увеличение затрат на разработку, поддержку и сопровождение; кроме того, она означает, что инструмент не справился с теми задачами, для которых он и был, согласно рассказам авторов, рассчитан.
Так как сочетание NIF-сокета с остальным Erlang означает, кроме того, что этот сокет должен кто-то написать и поддерживать (ладно, я бы написал, у меня "экспертизы" хватает, а дальше кто этим займётся?), ещё и отладку бутерброда и Erlang+C (а то, что отладка будет нужна — к гадалке не ходи). Это проблема всех бутербродных построений, и выигрывают те средства, в которых они не нужны как минимум для 99% базовой функциональности (вспоминаем Java, C#, Python...)
SD> можно было реализовать backpressure, можно было разобраться и поднастроить GC. Можно было подумать про архитектуру и ее огранчиения: почему у вас строго одна выходная "труба"? Ведь это single point of failure. Это по определению ненадежно.
Да вы уже, похоже, цепляетесь за всё подряд. Во втором проекте (я тут везде его так называл, пусть остаётся) единственность выходной трубы это условия вписывания во внешние условия задачи, их менять нельзя. Но даже если бы этого не было, абсолютно не вопрос надёжности, одна тут "труба" или нет: в случае любой потери надо перепосылать с точки обрыва, выкапывая первое недоставленное сообщение. И общей проблемой, наверно, 99% взаимодействий является то, что если сообщение потеряно, то его надо перепосылать. Множественные "трубы" тут ничего не дадут.
N>> Проблема в receive именно в количестве сообщений, а если я все передаваемые пакеты набью в одно, оно будет тяжёлым (объём данных никуда не девается), но одним. Потом процесс будет разбирать его и отправлять по кусочкам.
SD>На этом месте следовало бы остановиться и посмотреть, как именно сделан message passing для тяжелых сообщений (точнее, binaries внутри). Само сообщение будет крошечным, и будет содержать только ref-counter pointer на binary (который лежит вообще в отдельном аллокаторе).
Ага, то есть хорошо было бы ещё и в binary их перепаковывать, причём размера не меньше сколько-то (с какого размера там включается оптимизация)? И это на внутреннем взаимодействии (внутри одной ноды)? Вам не кажется, что это слишком большие требования для простой задачи?
SD>Позвольте мне пропустить дальнейшее описание рукопашного flow control protocol, ибо сама необходимость водружать столь сложный механизм была основна на заведомо неверном предположении. Ох неспроста "The Art of Challenging Assumptions" по статистике Whova App набрал максимум посещаемости из всех выступлений на той конференции
Я посмотрел, спасибо. Великолепный английский как для родного русского — завидую... остальное — или подробности Erlang-решений, или вода. Любой грамотный разработчик с опытом скажет то же самое, пусть и не столь красиво, но во всём этом нет ответа на главное: мы в принципе все свои действия основываем на предположениях на основании существующего опыта, и их подавляющее большинство на самом деле ничему не мешает, и менять их нереально и бессмысленно; но, после того, как надо просто выделить ресурсы на измерения и проверки того, что казалось очевидным (а ресурсы тут важны всегда, и сурово критичны — почти всегда) — остаётся определить, с чего именно начинать измерения и проверки, а это получается только с опытом в теме.
В сумме, доклад — красивый пиар вас лично и фирмы. Полагаю, что вы таки достигли поставленных для этого пиара целей.
И чем же тут было "заведомо неверное" предположение — вы не объяснили, так что ответа нет. Ответ в духе "вы предположили, что можно обойтись первыми приходящими в голову средствами и не подумать вызвать X, Y, Z, чёрта лысого, подключить библиотеку WWW243972" это не ответ, это издевательство.
N>>Вот это как раз тот случай, когда мы применили ETS, что вам так не нравится.
SD>Ну, в моей практике встречались и еще более странные применения ETS. Равно как и еще более необычные side channels — скажем, вместо ETS можно было бы завести отдельный процесс (условно назовем его locker) который бы отвечал на вопрос "уже можно или еще нет".
Дададад. "Отвечал". То есть шло бы обратно сообщение, которое вставало бы в конец очереди. Вы это серьёзно?
SD> Способов организвать flow control — не счесть. И не только на Эрланге, на Java/C++/Python есть ничуть не меньше разных забавных конструкций на эту тему. Скажем, проверять глобальные флажки. А что, отличный вопрос для собеседования — поговорить о семантике volatile переменных
И вы тут то ли намеренно ёрничаете, то ли явно не в курсе про volatile в многонитевом окружении?
SD>В общем, можно построить очень много ненужных велосипедов, если с самого начала не разобраться в задаче как следует.
SD>Но. Хочу отдать должное, даже в самых простых решениях вроде этого самого binary buffer можно очень легко сделать ошибку. Вот пример. Прям один-в-один ваш случай, включая gen_tcp:send, и middleman-process. Неспроста я про TLS и ужасы говорил
Перескажите словами, что происходит, если хотите понимания. Я-то ещё разберу происходящее за пару часов (TLS на Erlang никогда не заводил), но если кто ещё читает — то точно не поймёт.
N>>Теперь: у этой системы есть свой мониторинг мониторинга.
SD>Каждый такой пример "системы мониторинга" зачастую следствие неумения организовать supervision tree.
Как вы через супервизоры будете снимать набор количественных показателей работы приложения (текущие уровни, счётчики, и всё такое)? Или вы под словом "мониторинг" тут понимаете только
>> какие ошибки считать transient, какие permanent, и на каком периоде времени/частоте
?
Ню ню (tm)
Или опять ETS, который вы хотели бы устранить?
SD>Можно обсудить эти вопросы более предметно, но уж шибко много печатать придется. В общем и целом, я всегда буду задавать один и тот же вопрос — "для чего", и "какие действия принимаются на основе полученных фактов".
Я думаю, последние 20 лет вопрос "какие действия принимаются..." на основе счётчиков событий (банально, полученных/посланных сообщений по выбранной классификации, и всё такое) — просто не должен озвучиваться. Вопрос должен быть — делаются ли эти действия вообще и правильные ли они.
N>>Просто: пусть у нас 3 очереди
SD>Нет, это не просто. Это как раз сложно. Просто — это когда у вас 3 процесса, и у каждого своя очередь.
Докажите. Я объяснил, что именно достигается несколькими очередями и как оно стабилизирует работу системы. Теперь хочу послушать, каким образом вы этими 3 раздельными процессами добьётесь аналогичного результата. На всякий случай приведу ещё раз базовые условия, хотя бы в таком наборе:
1) Речь идёт об управлении конкретным процессом и его контроле через единственный, по вашим же правилам, разрешённый интерфейс в виде асинхронно поступающих сообщений. Канал через ETS не разрешаем.
2) Надо управлять этим процессом, включая регулирование параметров (оперативное! ждать секунды, пока он прочтёт сообщение, нельзя).
3) Увы, gen_call иногда (а может, и всегда) нужен из-за того, что на нём работает некоторый интерфейс, который мы менять не можем.
Если вы скажете, что надо заставлять источники группировать сообщения в большие бинарные пакеты, как выше — то это не подходит потому, что в этом случае тем более нафиг не нужно 3 процесса.
N>>И, что крайне важно, это всё не помешало бы всему остальному, что вы тут пишете про "правильный дизайн".
SD>Думаю, что ваша точка зрения не имеет достаточной научной базы.
О том, что не помешало бы? То есть у вас есть очень себе "challenging" предположение о том, что чем-то помешало бы? Хочу послушать предпосылки к этому предположению, это становится интересным
SD>Понимаю, что у вас есть вполне конкретный случай, конкретная проблема, но вот что неверно в ваших рассуждениях, вы пытаетесь решить ее не на том уровне. Не скажу что вы одиноки в таком подходе
И снова от вас никакой конкретики, только "стройная модель" (стройность которой на самом деле ничуть не испортит введение нескольких очередей) и ссылка на какие-то посторонние случаи, про которые ничего не известно.
N>>И от синхронных вызовов отказаться как-то нереально. Хотя сократить их долю, да, можно.
SD>Представляете, что было бы, если бы ваш мозг мог синхронно зависнуть, ожидая ack'а от сердечной мышцы?
Плохая аналогия подобна котёнку с дверцей (tm).
Ваша аналогия с организмом, повторюсь, неадекватна хотя бы потому, что в сердце и мозгу нет "процессов", аналогичных Erlangʼовым, там всё иначе. Но если вы уж начали обсуждать такими аналогиями, почитайте про основные проблемы анестезии и почему является проблемой, что регулировка дыхания мозгом неадекватна и в какой-то момент начинает работать против своей цели.
N>>Вот именно, что ищет решения. Нашим решением для второго проекта был уход от Erlang. И не пожалели.
SD>Очень разумный подход.
Если задача в том, чтобы "быстрее сделать", выбирать надо тот инструмент, в котором разбираешься лучше всего.
А почти все задачи начинаются с "быстрее сделать", построить MVP или какой там будет его аналог. И вот тут становится критичным, что люди, которые не разбираются во всех тонкостях, должны поднять и сопровождать проект на начальной стадии.
Про кривую обучения вы сами вспомнили. И именно ваша совсем не пологая кривая (за пределами задач из песочницы) тут очень мешает, что в итоге уходят на другой инструмент. И вы отказываетесь применить простые средства избавления от этой крутизны, ссылаясь на "отсутствие научной базы" (которую сами же и отказываетесь формировать).
Ну раз вы сами подтвердили твёрдую позицию команды разработчиков и спутников, как вы — значит, ничего хорошего ожидать от инструмента далее не приходится. Остаток сообщения это подтверждает.
SD>Вы описали один из test cases для off-heap message queue. В 2010 ее не было (появилась в OTP 19, то есть 5 лет назад).
То есть пробовать не хотите? Спасибо, Ч.Т.Д.
SD>К делу не относится, но здесь мне хочется поддержать Pzz,
SD>
SD>Вот это — чертова проблема. Я отношусь к тем людям, которые впятером могут гору свернуть, а потом аккуратно поставить ее на место, а жить приходится в мире, рассчитанном на массовое трудоустройство альтернативно умственно одаренных людей.
Ну поддерживайте себе дальше. Мне уже скорее пофиг, я понял, что от Erlang тут толку не добиться, по крайней мере, пока основной состав разработчиков и управляющих (как бы вы их ни называли) не сменится. Но это, увы, типовая судьба любых проектов — их доводят до ума самые упёртые, а не отличающиеся широтой и глубиной взглядов.
А для практического применения я буду выбирать средства с максимально пологой кривой обучения (будем уже держаться в рамках этого термина), потому что полученные продукты будут выживать даже при полной смене команды.
Ну и у меня, по своему опыту, далеко не такие мрачные выводы о качестве людей (по крайней мере программистов). Может, потому, что я никогда не работал в аутсорсе.
SD>Это ваши личные домыслы. Я лишь утверждаю, что вне зависимости от языка или парадигмы, разработчик, который в состоянии challenge assumptions, который может провести root cause analysis, а затем решить проблему, на порядок более производителен "среднего качества разработчика".
Ну вот пока вы считаете "средним качеством" тех, кто не может и даже не хочет... ok, ваше дело.