На самом деле, для простого программиста на Python, GIL не решает ни каких проблем при разработке мультипоточных приложений. В статье не совсем корректно об этом «заявляется» (или косяк перевода).
GIL защищает не вашу программу на питоне, а только состояние самого интерпретатора. Вам же, как и обычно, надо использовать в мультипоточной программе разного вида локи, что бы бы избежать параллельного доступа к общим данным.
В питоне просто нет отдельного типа данных для хранения одного символа (юникодный аналог char из C). Поэтому даже один символ — это строка. И хоть __iter__ и итерируется по «символам», но возвращает он всётаки строки.
Совсем другое дело с типом bytes, для его элементов в питоне есть специальный тип — byte. И для него всё как бы нормально — итератор возвращает «числа», а не bytes с длиной в один байт.
"join" и "транзакции" в монге — это такие маркетинговые словечки, которые лучше вообще не ставить в один ряд с теми, что есть, например, в Postgres-е.
Нечто вроде join-а в монге (lookup) доступно только при выполнении агрегаций. А они не во всех случаях подходят. И точно не получится это использовать, например, для обновления данных.
Транзакции в монге накладывают такие ограничения, что полностью пропадает главный плюс — простое шардирование в монге (транзакции не работают на шардированных коллекциях). И работают они только при использовании репликации. На простой, запущенной в локалхосте, в единственном экземпляре, монге — транзакции не работают. Ну и конечно же их использование существенно замедляет обработку запросов.
В живую Fluent я ещё не использовал, но зато пользовался gettext. И после того, что я узнал про возможности Fluent, использовать gettext у меня больше нет желания.
Я программист и мой первый шок от gettext был когда я понял, что для получения перевода с поддержкой множественных форм, я должен в коде использовать отдельную функцию и передать ей варианты написания куска текста, зависящего от числа. Как так? Зачем я, как программист, должен думать про множественные формы?
Позднее мне стало понятно почему так получилось в gettext — потому что он разрешает использовать текст как id сообщения, а значит должен уже в коде приложения иметь всю информацию необходимую для генерации текста, даже если вообще нет ни каких переводов.
В Fluent с этим всё становится легко и просто. Для получения перевода в коде используется только одна функция, и мне не надо при этом думать про множественные формы даже для одного (например английского) языка. А ещё в Fluent (в отличии от gettext) можно в рамках одного сообщения использовать несколько разных чисел для выбора множественной формы разных частей строки. А можно использовать вообще не числа, а например пол «персонажа» и вообще любой другой «селектор», который может повлиять на перевод.
С Fluent в принципе не нужны инструменты для извлечения «строк» из кода, не надо их мержить с уже имеющимися переводами, т.к. в коде есть только id сообщений. Тут конечно есть минус — можно забыть добавить id в переводы если у вас нет тестов в приложении, которые вероятно выдадут ошибку при запросе не существующего перевода.
По поводу инструментов для работы с переводами — у Mozilla есть в гитхабе опен-сорсное веб-приложение для совместной работы над переводами (они используют его для перевода своих проектов), название не помню, но в одной с предыдущих статей про Fluent на хабре оно упоминалось.
Как я написал в первом ответе, наиболее типичным вариантом «сессией с состоянием» как правило является нечто вроде привязки состояния к TCP-соедниению между клиентом и сервером, а не то что у вас в базе данных на сервере или в клиенте хранится. Храните в базе что хотите и как хотите, передавайте информацию от клиента к серверу тоже как хотите (через куки, через тело запроса, через другие заголовки) — главное передавайте в каждом запросе если они ему нужны.
Если рассуждать как вы описали выше, то вообще любое хранение сервером данных — это нарушение REST, т.к. это хранение «состояния». Но это не так, т.к. это совершенно другое «состояние». Это состояние сервера. У клиента тоже есть какое-то своё состояние и он посредством запросов к серверу пытается «синхронизировать» своё состояние с тем, что есть на сервере. При этом ни сервер, ни клиент не должны полагаться на то, что за сколь угодно малое время между двумя запросами, состояние одного из них не изменилось. Архитектура должна проектироваться так, как будто между двумя запросами и даже одновременно с ними, происходят другие процессы меняющие «локальное» состояние участников процесса. Отсюда и происходит требование, что бы в запросе передавалась вся необходимая для его выполнения информация. И не важно в каком она виде, как называется и каким образом обрабатывается сервером. Главное что серверу будет достаточно этой информации.
Попробую привести «кухонный пример» почему stateless важен для построения масштабируемой архитектуры.
Представьте кинотеатр, кассы, билетёр на входе в зал. Вы купили билет, показали билетёру и вошли в зал. Потом решили вернуться в машину за забытыми очками. Если, когда вы возвращались, билетёр ОБЯЗАН запомнил вас в лицо и пустить без проверки билета — это не масштабируемая система. Такого билетёра нельзя подменить другим, потому что он вас не знает и не сможет впустить. Вам придётся снова проходить процедуру покупки билета, если вы его потеряли, и показывать его билетёру.
Если же билетёр ОБЯЗАН пропускать только по билету — то любой билетёр сможет пропустить вас с билетом, даже если видит вас впервые. Вот это масштабируемая схема — можно поставить хоть 10 билетёров и вы можете к любому подойти со своим билетом, а не только к тому, кто вас первым проверил.
Ну если у разных серверов есть доступ к общей базе с этими «restricted-данными», то скорее всего в этой же (или соседней) базе есть данные необходимые для авторизации клиента. Например есть таблица с мапингом токенов на юзеов. Клиент в этом случае передаёт в каждом запросе свой авторизационный токен, который получил от сервера в процессе аутентификации.
Ну или всё ещё проще — клиент в каждом запросе передаёт свой логин-пароль, как это сделано в HTTP Basic авторизации.
Потому что вы не правильно понимаете это слово. Под «состоянием» имеется ввиду состояние некой «сессии» между клиентом и сервером. Например такой «сессией» может быть подключение через TCP. Как только оно разрывается, то «сессия» завершается, состояние теряется. При следующем подключении надо это состояние опять восстанавливать: выполнять аутентификацию, давать команды для перехода в нужную директорию (как в FTP) и др.
Если же у вас «stateless» — то каждый запрос от клиента передаёт всю необходимую информацию не полагаясь на то, что сервер «помнит» клиента. В общем случае запросы клиента могут с помощью балансировщика направляться на разные сервера, какая тогда вообще может быть речь про «сессию» и «состояние сессии»?
Я как то попробовал переключать раскладку Caps-ом. Но то-ли в Ubuntu что-то криво сделано или я такой быстрый. Частенько получалось, что я нажимал букву ещё до того как полностью отпустил Caps — в результате вводилась буква в верхнем регистре.
Ну и конечно невозможность без внешних приложений настроить Caps для Windows, не добавляет «балов» такому способу.
Ох, надо внимательнее читать.
Я решил что вы про простую, неожиданную ошибку при работе с базой данных, а не про конфликтный доступ к ресурсу, который предусмотрен бизнес-логикой. В последнем случае действительно надо использовать статус 409.
Да, я согласен что это RFC для расширения HTTP, и я видимо не совсем в этом плане был корректен. Но в целом не так важно в каком RFC это находится, важнее то что все эти RFC делаются и контролируются примерно в одном месте. И значит завтра не появится RFC на HTTP 4.0 в котором будет код 422 и он будет означать что-то другое.
Другой важный момент в самом наличии этих RFC и других, более «попсовых», ресурсов с документацией (например MDN и Википедия) в которых всё это задокументировано. Наличие такой публично доступной документации — очень большой плюс. Как минимум это облегчает «муки» выбора — всё уже придумано до нас, и очевидно не совсем глупыми людьми.
Я правильно понял, что ваш пример с Заявкой — это пример который, как вы считаете, плохо укладывается в HTTP (или REST)?
Разделение ресурсов между потребителями — это как раз одна из задач, которую помогают решить принципы REST и в частности HTTP, можно сказать что они создавались с учётом этого. Ваш пример отлично решается силами HTTP — с помощью условных полей заголовка (If-Match и др) + упомянутый вами статус 412, который как раз относится к этим заголовкам.
При выборе группы для ошибки (4xx — 5xx) в первую очередь дайте ответ на вопрос "Кто виноват?".
У вас во втором пункте виноват клиент — это его ошибка, что он попытался добавить товар с существующим в базе ИНН. Следовательно сразу становится понятно, что надо выбрать код из группы 4xx.
PS: Коды со статусами < 400 формально не являются статусами описывающими ошибки.
409 будет верным ответом только в случае если формально клиент допустил ошибку сделав запрос, который не может быть выполнен по причине определённых бизнес-требований вызывающих конфликт (например "нельзя удалить файл если он в данный момент кем-то открыт")
409 Conflict — запрос не может быть выполнен из-за конфликтного обращения к ресурсу. Такое возможно, например, когда два клиента пытаются изменить ресурс с помощью метода PUT.Появился в HTTP/1.1.
The 500 (Internal Server Error) status code indicates that the server encountered an unexpected condition that prevented it from fulfilling the request.
Клиенту абсолютно всё равно, что у вас там внутри — база данных, текстовые файлы или просто бага в коде. Для клиента ваше приложение это "чёрный ящик". И если внутри "ящика" происходит что-то неожиданное, что не предусмотрено бизнес-логикой — это "внутренняя ошибка сервера".
PS: статусы семейства 4xx — это статусы описывающие ошибку клиента, они не подходят. Если конечно вы не хотите обвинить клиента в том, что у вас база данных упала.
Почитал коменты и в очередной раз убедился, что большинство работающих с HTTP плохо его знают, не читали RFC или просто не стали вникать в полученную информацию как будто это нудное нравоучение старика-маразматика.
Правильный статус на такую ошибку — 422 Unprocessable Entity.
The 422 (Unprocessable Entity) status code means the server understands the content type of the request entity (hence a 415(Unsupported Media Type) status code is inappropriate), and the syntax of the request entity is correct (thus a 400 (Bad Request) status code is inappropriate) but was unable to process the contained instructions.
Статус 400 (Bad Request) означает, что в теле запроса находится абсолютно не то что ожидает сервер. Например вы сообщили ему в Content-Type что передаёте JSON, а в тело запихнули XML или просто не валидный JSON.
Если вы хотите писать приложение соблюдая принципы REST и с API поверх HTTP, то грех не воспользоваться хорошо продуманным решением построенным на тех же принципах и для которого (и это очень важно) есть документация в виде RFC и кучи статей в интернетах. В этом случае вам не придётся изобретать велосипед и придумывать свою систему ошибок, не придётся детально описывать все нюансы принятых вами решений, «новичкам» будет проще ориентироваться в вашем API т.к. они скорее всего уже знают про HTTP и его «систему ошибок». В настоящий RESTful API очень легко и гармонично вписываются HTTP ошибки.
Если же у вас не REST, и ресурсы не являются ключевым элементом вашего API, то скорее всего ваши ошибки будут плохо укладываться в систему придуманную для HTTP и тогда вам действительно будет проще ограничится минимальным набором кодов ответа, а всё остальное реализовать другими средствами (хоть через кастомные HTTP заголовки).
PS: Существенная часть HTTP статусов не связана с «транспортным уровнем» или «веб-сервером». Много статусов относятся к бизнес логике.
По моему тут опять сравнили мягкое с пушистым. Как можно сравнивать скорость работы по числу запросов в секунду, если сравнивается всего ДВА воркера для синхронных фреймворков с условно бесконечным числом «воркеров» для асинхронных?
В таком тесте корректнее сравнивать время на обработку одного запроса. По этому показателю в этом тесте победил Django.
А если хочется сравнить число запросов в секунду, то надо значительно увеличить число воркеров для синхронных фреймворков. И заодно собрать статистику как от числа воркеров зависит число запросов в секунду и объём потребляемой памяти.
GIL защищает не вашу программу на питоне, а только состояние самого интерпретатора. Вам же, как и обычно, надо использовать в мультипоточной программе разного вида локи, что бы бы избежать параллельного доступа к общим данным.
Да, с byte это я нагнал конечно, не проверил.
__iter__и итерируется по «символам», но возвращает он всётаки строки.Совсем другое дело с типом bytes, для его элементов в питоне есть специальный тип — byte. И для него всё как бы нормально — итератор возвращает «числа», а не bytes с длиной в один байт.
"join" и "транзакции" в монге — это такие маркетинговые словечки, которые лучше вообще не ставить в один ряд с теми, что есть, например, в Postgres-е.
Нечто вроде join-а в монге (lookup) доступно только при выполнении агрегаций. А они не во всех случаях подходят. И точно не получится это использовать, например, для обновления данных.
Транзакции в монге накладывают такие ограничения, что полностью пропадает главный плюс — простое шардирование в монге (транзакции не работают на шардированных коллекциях). И работают они только при использовании репликации. На простой, запущенной в локалхосте, в единственном экземпляре, монге — транзакции не работают. Ну и конечно же их использование существенно замедляет обработку запросов.
Я программист и мой первый шок от gettext был когда я понял, что для получения перевода с поддержкой множественных форм, я должен в коде использовать отдельную функцию и передать ей варианты написания куска текста, зависящего от числа. Как так? Зачем я, как программист, должен думать про множественные формы?
Позднее мне стало понятно почему так получилось в gettext — потому что он разрешает использовать текст как id сообщения, а значит должен уже в коде приложения иметь всю информацию необходимую для генерации текста, даже если вообще нет ни каких переводов.
В Fluent с этим всё становится легко и просто. Для получения перевода в коде используется только одна функция, и мне не надо при этом думать про множественные формы даже для одного (например английского) языка. А ещё в Fluent (в отличии от gettext) можно в рамках одного сообщения использовать несколько разных чисел для выбора множественной формы разных частей строки. А можно использовать вообще не числа, а например пол «персонажа» и вообще любой другой «селектор», который может повлиять на перевод.
С Fluent в принципе не нужны инструменты для извлечения «строк» из кода, не надо их мержить с уже имеющимися переводами, т.к. в коде есть только id сообщений. Тут конечно есть минус — можно забыть добавить id в переводы если у вас нет тестов в приложении, которые вероятно выдадут ошибку при запросе не существующего перевода.
По поводу инструментов для работы с переводами — у Mozilla есть в гитхабе опен-сорсное веб-приложение для совместной работы над переводами (они используют его для перевода своих проектов), название не помню, но в одной с предыдущих статей про Fluent на хабре оно упоминалось.
Как я написал в первом ответе, наиболее типичным вариантом «сессией с состоянием» как правило является нечто вроде привязки состояния к TCP-соедниению между клиентом и сервером, а не то что у вас в базе данных на сервере или в клиенте хранится. Храните в базе что хотите и как хотите, передавайте информацию от клиента к серверу тоже как хотите (через куки, через тело запроса, через другие заголовки) — главное передавайте в каждом запросе если они ему нужны.
Если рассуждать как вы описали выше, то вообще любое хранение сервером данных — это нарушение REST, т.к. это хранение «состояния». Но это не так, т.к. это совершенно другое «состояние». Это состояние сервера. У клиента тоже есть какое-то своё состояние и он посредством запросов к серверу пытается «синхронизировать» своё состояние с тем, что есть на сервере. При этом ни сервер, ни клиент не должны полагаться на то, что за сколь угодно малое время между двумя запросами, состояние одного из них не изменилось. Архитектура должна проектироваться так, как будто между двумя запросами и даже одновременно с ними, происходят другие процессы меняющие «локальное» состояние участников процесса. Отсюда и происходит требование, что бы в запросе передавалась вся необходимая для его выполнения информация. И не важно в каком она виде, как называется и каким образом обрабатывается сервером. Главное что серверу будет достаточно этой информации.
Попробую привести «кухонный пример» почему stateless важен для построения масштабируемой архитектуры.
Представьте кинотеатр, кассы, билетёр на входе в зал. Вы купили билет, показали билетёру и вошли в зал. Потом решили вернуться в машину за забытыми очками. Если, когда вы возвращались, билетёр ОБЯЗАН запомнил вас в лицо и пустить без проверки билета — это не масштабируемая система. Такого билетёра нельзя подменить другим, потому что он вас не знает и не сможет впустить. Вам придётся снова проходить процедуру покупки билета, если вы его потеряли, и показывать его билетёру.
Если же билетёр ОБЯЗАН пропускать только по билету — то любой билетёр сможет пропустить вас с билетом, даже если видит вас впервые. Вот это масштабируемая схема — можно поставить хоть 10 билетёров и вы можете к любому подойти со своим билетом, а не только к тому, кто вас первым проверил.
Ну или всё ещё проще — клиент в каждом запросе передаёт свой логин-пароль, как это сделано в HTTP Basic авторизации.
Если же у вас «stateless» — то каждый запрос от клиента передаёт всю необходимую информацию не полагаясь на то, что сервер «помнит» клиента. В общем случае запросы клиента могут с помощью балансировщика направляться на разные сервера, какая тогда вообще может быть речь про «сессию» и «состояние сессии»?
Ну и конечно невозможность без внешних приложений настроить Caps для Windows, не добавляет «балов» такому способу.
Я решил что вы про простую, неожиданную ошибку при работе с базой данных, а не про конфликтный доступ к ресурсу, который предусмотрен бизнес-логикой. В последнем случае действительно надо использовать статус 409.
Другой важный момент в самом наличии этих RFC и других, более «попсовых», ресурсов с документацией (например MDN и Википедия) в которых всё это задокументировано. Наличие такой публично доступной документации — очень большой плюс. Как минимум это облегчает «муки» выбора — всё уже придумано до нас, и очевидно не совсем глупыми людьми.
Разделение ресурсов между потребителями — это как раз одна из задач, которую помогают решить принципы REST и в частности HTTP, можно сказать что они создавались с учётом этого. Ваш пример отлично решается силами HTTP — с помощью условных полей заголовка (If-Match и др) + упомянутый вами статус 412, который как раз относится к этим заголовкам.
При выборе группы для ошибки (4xx — 5xx) в первую очередь дайте ответ на вопрос "Кто виноват?".
У вас во втором пункте виноват клиент — это его ошибка, что он попытался добавить товар с существующим в базе ИНН. Следовательно сразу становится понятно, что надо выбрать код из группы 4xx.
PS: Коды со статусами < 400 формально не являются статусами описывающими ошибки.
409 будет верным ответом только в случае если формально клиент допустил ошибку сделав запрос, который не может быть выполнен по причине определённых бизнес-требований вызывающих конфликт (например "нельзя удалить файл если он в данный момент кем-то открыт")
Однозначно 500 Internal Server Error
Клиенту абсолютно всё равно, что у вас там внутри — база данных, текстовые файлы или просто бага в коде. Для клиента ваше приложение это "чёрный ящик". И если внутри "ящика" происходит что-то неожиданное, что не предусмотрено бизнес-логикой — это "внутренняя ошибка сервера".
PS: статусы семейства 4xx — это статусы описывающие ошибку клиента, они не подходят. Если конечно вы не хотите обвинить клиента в том, что у вас база данных упала.
Почитал коменты и в очередной раз убедился, что большинство работающих с HTTP плохо его знают, не читали RFC или просто не стали вникать в полученную информацию как будто это нудное нравоучение старика-маразматика.
Правильный статус на такую ошибку — 422 Unprocessable Entity.
Статус 400 (Bad Request) означает, что в теле запроса находится абсолютно не то что ожидает сервер. Например вы сообщили ему в Content-Type что передаёте JSON, а в тело запихнули XML или просто не валидный JSON.
Если же у вас не REST, и ресурсы не являются ключевым элементом вашего API, то скорее всего ваши ошибки будут плохо укладываться в систему придуманную для HTTP и тогда вам действительно будет проще ограничится минимальным набором кодов ответа, а всё остальное реализовать другими средствами (хоть через кастомные HTTP заголовки).
PS: Существенная часть HTTP статусов не связана с «транспортным уровнем» или «веб-сервером». Много статусов относятся к бизнес логике.
В таком тесте корректнее сравнивать время на обработку одного запроса. По этому показателю в этом тесте победил Django.
А если хочется сравнить число запросов в секунду, то надо значительно увеличить число воркеров для синхронных фреймворков. И заодно собрать статистику как от числа воркеров зависит число запросов в секунду и объём потребляемой памяти.