Что вы имеете ввиду под "параллельностью"? Одновременное выполнение? Если она у вас уже есть, тогда зачем вам потоки? А если, как я понял, у вас "автоматы в одном потоке", то нет у вас ни какой параллельности - у вас конкурентное выполнение (одновременно работает только один кусок кода).
Поводу того, что бы объединить "автоматы" и "потоки" - это вам прямой наводкой в современные асинхронные фреймворки. Они позволяют запускать "корутины" как в одном потоке, так и распределять их выполнение по разным потокам. Не знаю на счёт таких фреймворков в C++ и как они дают гарантии, что не будет проблем с выполнением корутин в разных потоках (наверное ни как - программист должен сам за этим следить). Но как минимум в Rust есть фреймворк tokio и он за счёт особенностей языка позволяет на этапе компиляции предотвратить попытку запуска в другом потоке кода, с которым нельзя так делать (который не обеспечивает мультипоточной безопасности).
Синхронизация потоков возможна только относительная, всё равно, хотя бы очень мало, почти не заметно, но они будут работать в разнобой. Определённо на такую "синхронность" не стоит рассчитывать в критических задачах. Особенно если потоков много и у каждого из них есть свой собственный "флаг" с разрешением на работу. Последний поток будет отставать от первого как минимум на то время которое выполняется цикл для перебора всех потоков, что бы выставить им "флаг" в true. Ну или надо очень угадать с задержкой в usleep(), что бы потоки "просыпались" после того, когда все флаги уже проставлены.
"Параллельная" работа в одном потоке - это, как я понял, сарказм? :-) В рамках одного потока не возможна параллельная работа. Параллельная в смысле одновременная. Можно только организовать конкурентную многозадачность тем или иным способом. При использовании автоматов в одном потоке или их варианта, который называют асинхронными фреймворками (корутины и всё такое) очень легко контролировать отсутствие "одновременного" доступа к общему ресурсу. При этом не нужны будут медленные мьютексы.
И насчёт запуска тысячи потоков - это вы конечно, что называется, "психанули". Определённо на средне-статистическом 16-ядерном процессоре пользы от этого совершенно ни какой. Даже наоборот, если эти потоки будут со своими мьютексами конкурировать за процессор, то они будут ещё сильнее мешать тому единственному потоку, который делает работу.
Результат ваших изысканий был предсказуем и без реальных тестов. Одно дело, когда у вас один "работник" просыпается по будильнику, заходит в кабинет, быстро делает свою задачу и уходит. И совсем другое когда у вас десяток таких работников, и все они одновременно долбятся в одну дверь кабинета, толкаются и мешают друг другу. Потоки не помогут для решения задачи, которую надо решать исключительно в одно "лицо". Если у вас именно такая задача и она одна - значит вам не нужны потоки. Если же у вас есть много задач, которые не мешают друг другу - потоки могут быть полезны.
Словил очередной диссонанс от вашей статьи. А в это раз ещё и от коментов к ней. Ощущение что и статью и коменты писал ChatGPT. Или может вы просто издеваетесь/прикалываетесь над читателями? Как можно заявлять о нескольких десятках лет опыта разработки программ и при этом выдавать такие наивные куски кода?
Листинг 3 Ваши потоки будет молотить воду в ступе нагружая ядро процессора пустым циклом, пока какой-то внешний "контроллер" не выставит bIfExecuteStep в true. Но это только на один прогон тела цикла, дальше опять будет бессмысленая пустая работа. Разве что вы укажете подходящую задержку для usleep, чтобы она примерно соответствовала частоте работы "контроллера", который разрешает потоку делать полезную работу.
Листинг 4 Поздравляю, вы доказали что 9 женщин не родят ребёнка за один месяц. Все ваши тысячи потоков будут бессмысленно греть возух в ожидании снятия лока в мьютексе или семафоре. А полезную работу по прибавлению единицы к счётчику всегда будет делать в данный момент времени только один поток, который "захватил" лок.
Если ваши алгоритмы перестают нормально работать в потоках, то скорее всего проблема в том, что вы в этих потоках меняете слишком много общих данных. А попытка защитится от всяких race-ов с помощью мьютексов даёт ужасный результат, т.к. почти вся работа потока у вас закрыта мютексом. Это неправильный подход, если вы хотите ускорения от потоков. Мьютексы должны использоваться точечно и на очень короткое время по сравнению с временем, которое поток тратит на решение задачи. Ваш код со счётчиком отличный пример того как не надо делать. В нём инкремент счётчика занимает 0% времени, а 99.(9)% занимает возня с мьютексами.
Вам бы без QT разобраться с тем как работают потоки и процессоры, а то больше похоже на то, что вы месяц воевали с UI фреймворком, а не с потоками.
Давно перешёл на новй UI. Из того что мне не понравилось в нём:
одинаковый цвет фона для кода и для колонки с номерами строк. Иногда не могу визуально понять где находится начало строки, если где-то за пределами видимой области есть брейкпоинты. Причём это не особенность темы, как я понял там в принципе используется для этого цвета одна и та же настройка, вместо двух отдельных.
не корректно работает кастомизация тулбаров. В настройке можно добавить кнопку, но на тулбаре она не будет отображаться. Например нельзя добавить кнопку "Show diff" в тулбар окна VCS (Git), которая там была в старом UI по умолчанию.
А как заставить ноутбук автоматически включиться после продолжительного отсутствия электропитания, когда батарейка успела разрядится? В этом плане штуки вроде Raspberry выглядят надёжнее.
AX3 из коробки идут с 7-ой версией и с новой реализацией пакета для работы с WiFi (который в UI так и называется "WiFi" вместо старого "Wireless"). Поэтому на AX3 не должно быть вообще каких-то проблем, т.к. нет ни какой "миграции" при обновлении.
В вашем примере всё таки есть синхронизация серверов, хоть и медленная. Значит эту задачу можно решить с использованием грамотного UI на клиенте. Который сможет донести до юзера информацию, что его запрос всё ещё находится в обработке и надо подождать. Можно ведь рассчитать максимальное время, требуемое на синхронизацию серверов и сообщить клиенту в ответе на первый запрос, сколько времени надо что бы информация о состоянии задачи была достпна на всех серверах.
Нельзя использовать гео-балансировку по DNS? Это что за такое странное требование к гео-распределённому сервису? Ок, если на один домен - один адрес, тогда можно сделать 100500 доменов. А что не так с тем что балансировщик знает про все шарды? Если у вас в домене только один адрес, а серверов много, то очевидно придётся делать на этом адресе балансер по всем серверам.
И ещё раз повторю - эти проблемы не имеют ни какого отношения к stateless из REST. Stateless ни помогает их решить, ни делает их решение сложнее.
Всё это имеет отношение только к тому как реализовать доступ клиента к результатам запроса, которые очень долго генерируются. И тут нет серебрянной пули. Особенно если, метафорически, запрос клиент послает в гугл, а результат ожидает от яндекса.
Так проблема в рандомной балансировке + отсутствии или очень медленной репликации данных. Надо либо сделать умный роутинг запроса, что бы он направлялся в нужный сервер, а не в рандомный (всё уже придумано до нас и есть разные варианты). Либо обеспечить быструю репликацию базы данных на все сервера, но это несколько примитивный способ, который будет в пустую тратить ресурсы.
PS: SOAP - как раз таки stateful протокол, но к данной проблеме это ни как не относится. Точно такую же проблему можно получить с чем угодно, если не поавильно спроектировать архитектуру под условия гео-распределённого сервиса.
А если пуш не дойдёт до клиента, что тогда делать? Видимо сохранить сообщение на сервере (желательно персистентно если это важное сообщение), придумать как определять, что нужный клиент вернулся и запушить ему повторно, даже если клиенту уже не нужно это сообщение. Т.е. получается то же самое только в профиль. Пушинг или пулинг - это разные подходы для решения одной задачи. Они имеют свои плюсы и свои минусы. Пулинг проще реализовать, особенно на серверной стороне. Пушинг обеспечивает более быструю доставку информации до клиента, меньше тратит трафика, но требует более сложных решений на сервере и клиенте.
И какие ограничения в масштабировании накладывает stateless? По мне так наоборот становится проще - не надо решать пачку проблем для обеспечения работы в statefull режиме. В stateless вообще же ничего не надо делать специального. Принял запрос, достал из него "инструкции", выполнил, вернул ответ и забыл.
"Асинхронные" запросы поверх rest ничем не отличаются от других. Мы создали какой-то ресурс в сервисе и позднее запрашиваем состояние этого ресурса. Обычная рутина для любого api. А послать запрос и ждать ответ пока не вернёт - это вообще почти не рабочая схема в условиях работы с сетью, т.к. в любой момент связь может прерваться. А значит, что-бы это решить, придётся на клиенте запоминать какой-то уникальный идентификатор, что бы после реконекта запросить у сервера результат обработки своего запроса. Т.е. мы сделаем то же самое что и в "асинхронных" запросах.
Вы продолжаете какую-то другую задачу решать. Stateless не про то что любой сервер в кластере должен уметь обработать запрос клиента. Он только про то, что клиент должен передать в запросе всю информацию, которая нужна, что бы сервис мог обработать этот запрос. Какой именно сервер будет обрабатывать запрос, и должн ли быть это любой сервер в кластере - это не относится к тому, что подразумевается под stateless в REST. Это другая задача, которая имеет разные решения. Начиная от полной, синхронной репликации базы продолжая шардированием с "умным" роутингом до нужной шарды и заканчивая такой реализацией где вообще не нужна база данных и любой сервер может обработать запрос клиента.
С учётом того, что репрезентация ресурса может быть совершенно разной, то я не думаю, что Филдинг закладывал в REST ограничение на то, что может возвращать и принимать сервер. В самом класическом виде сервер может возвращать ресурс в виде html страницы, а принимать его в виде html формы. Так же ресурс может возвращаться сервером как картинка (например график), а создаваться передачей json-а со списком точек. Это всё настолько "особенности реализации", что не стоят упоминания в фундаментальных ограничениях архитектуры (даже не приложения или протокола), которые разработал Филдинг.
Такие доп. ограничения можно вводить самостоятельно из соображений удобства разработки конкретного приложения. Например с ними проще сделать фреймворк. Но я не считаю их фундаментальными, что бы строго следовать им в любом приложении, которое претендует на тег "REST".
Я не вижу в вашей цитате Филдинга ограничение на то, что репрезентация передаваемая клиентом на сервер должна быть ограничена репрезентацией, которую возвращает сервер клиенту. Не думаю что это хоть чем-то важно в контексте REST
Вы немного про другое спрашиваете. Можно ли получить состояние от другого сервера или нет - это не имеет отношения к stateless. Stateless исключительно про то, что клиент должен в своём запросе передать всю необходимую информацию, что бы сервер мог его обработать. Вот пример общения которое рассчитано на наличие стейта между клиентом и сервером: Q: Сколько продуктовых магазино в Магадане? A: 543 Q: А сколько обувных? A: 100
Видите где тут стейт? Второй запрос не содержит "фильтра" по городам (в Магадане). Но т.к. сервер помнит стейт, то отвечает правильно.
Сделать такое не просто, если у нас больше одного сервера. Надо уметь определять в рамках какой сессии пришёл запрос от клиента. Надо как-то хранить "сессию" и как-то удалять её когда протухла. А клиент может создать 100500 сессий. Надо сделать эту сессию доступной на других серверах или обеспечить роутинг запроса так, что бы он попадал на те сервера где есть его сессия.
Примером "ужасного" протокола с состоянием является FTP - что бы попасть в какую-то папку надо по очереди послать команды на перемещение в следующую дочернюю папку. Сервер при этом помнит где находится клиент. После этого клиент может что-то делать в папке, в которую "зашёл". Если случиться разрыв связи, то клиенту придётся опять повторять всё что он делал до этого - пересещаться постепенно в нужную папку.
Не думаю, что это определение запрещает передать, что-то дополнительное, что не является прямым отражением на поля ресурса в базе данных или ещё где хранится ресурсв. Репрезентация может иметь любой вид как по форме так и по содержанию. На то она и репрезентация сущности, а не сама сущность. Например у ресурса может быть поле published с датой публикации. Но мы не хотим давать клиентам менять его напрямую. Для этого клиент может передавать "виртуальное" поле is_published: bool, а уже сервер будет в зависимости от него менять значение поля published.
stateles относится к тому, что клиент не должен рассчитывать на то, что сервер помнит его предыдущие запросы. Каждый новый запрос клиента должен содержать достаточно информации, что бы сервер мог его обработать. К серверу, в общем случае, это тоже относится - он не должен рассчитывать на то, что клиент помнит предыдущие ответы, он просто возвращает то что попросил клиент в текущем запросе. Такой подход как раз позволяет упростить масштабирование сервиса, т.к. нам не надо реплицировать "знание" о предыдущих запросах на все сервера, куда может прийти запрос клиента (вы сами привели этот довод на примере сотовых станций).
Рассмотрим ваш пример в условиях гео-распределённого сервиса. Первый запрос клиента создаёт задачу, сервис возвращает URL этой задачи. После чего клиент по этому URL-у запрашивает состояние задачи. И вот тут уже видно отсутствие "состояния". Клиент не держит открытым TCP конект, через который задача была создана, что бы через этот конект потом получать состояние этой задачи. Вместо этого клиент может создать новый конект и передать туда запрос с URL-ом. И этого URL-а будет достаточно сервису, что бы понять куда в большом кластере надо направить запрос для обработки. Во первых роутинг произойдёт на уровне DNS, направив запрос в тот дата-центр, где задача была создана. Далее, балансировщик в ДЦ по URL-у определит какая "шарда" хранит в себе информацию об этой задаче и отправит запрос туда. В результате нет ни какой необходимости делать реплики для базы данных. Можно обойтись лучше масштабируемым вариантом - шардированием.
Так же декларация использования stateless сообщает нам о том, что мы должны соответствующим образом вести разработку клиента. Клиент должен "понимать", что любой его запрос может завершиться не так как он ожидает, из-за того что сервис не гарантирует ему, что состояние его базы данных совпадает с "ожиданием" клиента. Хотя это скорее не про stateless, а общий принцип разработки мульти-пользовательских сервисов.
Что вы имеете ввиду под "параллельностью"? Одновременное выполнение? Если она у вас уже есть, тогда зачем вам потоки? А если, как я понял, у вас "автоматы в одном потоке", то нет у вас ни какой параллельности - у вас конкурентное выполнение (одновременно работает только один кусок кода).
Поводу того, что бы объединить "автоматы" и "потоки" - это вам прямой наводкой в современные асинхронные фреймворки. Они позволяют запускать "корутины" как в одном потоке, так и распределять их выполнение по разным потокам.
Не знаю на счёт таких фреймворков в C++ и как они дают гарантии, что не будет проблем с выполнением корутин в разных потоках (наверное ни как - программист должен сам за этим следить). Но как минимум в Rust есть фреймворк tokio и он за счёт особенностей языка позволяет на этапе компиляции предотвратить попытку запуска в другом потоке кода, с которым нельзя так делать (который не обеспечивает мультипоточной безопасности).
Синхронизация потоков возможна только относительная, всё равно, хотя бы очень мало, почти не заметно, но они будут работать в разнобой. Определённо на такую "синхронность" не стоит рассчитывать в критических задачах. Особенно если потоков много и у каждого из них есть свой собственный "флаг" с разрешением на работу. Последний поток будет отставать от первого как минимум на то время которое выполняется цикл для перебора всех потоков, что бы выставить им "флаг" в true. Ну или надо очень угадать с задержкой в usleep(), что бы потоки "просыпались" после того, когда все флаги уже проставлены.
"Параллельная" работа в одном потоке - это, как я понял, сарказм? :-)
В рамках одного потока не возможна параллельная работа. Параллельная в смысле одновременная. Можно только организовать конкурентную многозадачность тем или иным способом.
При использовании автоматов в одном потоке или их варианта, который называют асинхронными фреймворками (корутины и всё такое) очень легко контролировать отсутствие "одновременного" доступа к общему ресурсу. При этом не нужны будут медленные мьютексы.
И насчёт запуска тысячи потоков - это вы конечно, что называется, "психанули". Определённо на средне-статистическом 16-ядерном процессоре пользы от этого совершенно ни какой. Даже наоборот, если эти потоки будут со своими мьютексами конкурировать за процессор, то они будут ещё сильнее мешать тому единственному потоку, который делает работу.
Результат ваших изысканий был предсказуем и без реальных тестов. Одно дело, когда у вас один "работник" просыпается по будильнику, заходит в кабинет, быстро делает свою задачу и уходит. И совсем другое когда у вас десяток таких работников, и все они одновременно долбятся в одну дверь кабинета, толкаются и мешают друг другу.
Потоки не помогут для решения задачи, которую надо решать исключительно в одно "лицо". Если у вас именно такая задача и она одна - значит вам не нужны потоки. Если же у вас есть много задач, которые не мешают друг другу - потоки могут быть полезны.
Словил очередной диссонанс от вашей статьи. А в это раз ещё и от коментов к ней.
Ощущение что и статью и коменты писал ChatGPT.
Или может вы просто издеваетесь/прикалываетесь над читателями? Как можно заявлять о нескольких десятках лет опыта разработки программ и при этом выдавать такие наивные куски кода?
Листинг 3
Ваши потоки будет молотить воду в ступе нагружая ядро процессора пустым циклом, пока какой-то внешний "контроллер" не выставит bIfExecuteStep в true. Но это только на один прогон тела цикла, дальше опять будет бессмысленая пустая работа. Разве что вы укажете подходящую задержку для usleep, чтобы она примерно соответствовала частоте работы "контроллера", который разрешает потоку делать полезную работу.
Листинг 4
Поздравляю, вы доказали что 9 женщин не родят ребёнка за один месяц.
Все ваши тысячи потоков будут бессмысленно греть возух в ожидании снятия лока в мьютексе или семафоре. А полезную работу по прибавлению единицы к счётчику всегда будет делать в данный момент времени только один поток, который "захватил" лок.
Если ваши алгоритмы перестают нормально работать в потоках, то скорее всего проблема в том, что вы в этих потоках меняете слишком много общих данных. А попытка защитится от всяких race-ов с помощью мьютексов даёт ужасный результат, т.к. почти вся работа потока у вас закрыта мютексом. Это неправильный подход, если вы хотите ускорения от потоков. Мьютексы должны использоваться точечно и на очень короткое время по сравнению с временем, которое поток тратит на решение задачи.
Ваш код со счётчиком отличный пример того как не надо делать. В нём инкремент счётчика занимает 0% времени, а 99.(9)% занимает возня с мьютексами.
Вам бы без QT разобраться с тем как работают потоки и процессоры, а то больше похоже на то, что вы месяц воевали с UI фреймворком, а не с потоками.
Давно перешёл на новй UI. Из того что мне не понравилось в нём:
одинаковый цвет фона для кода и для колонки с номерами строк. Иногда не могу визуально понять где находится начало строки, если где-то за пределами видимой области есть брейкпоинты. Причём это не особенность темы, как я понял там в принципе используется для этого цвета одна и та же настройка, вместо двух отдельных.
не корректно работает кастомизация тулбаров. В настройке можно добавить кнопку, но на тулбаре она не будет отображаться. Например нельзя добавить кнопку "Show diff" в тулбар окна VCS (Git), которая там была в старом UI по умолчанию.
В статье формулировка не полная.
Доступ к приватным сущностям в расте есть из любого кода, который расположен в модуле не выше того, где определена приватная сущность.
При этом каким образом модули раскиданы по папкам и файлам - не имеет значение. Значение имеет только иерархия модулей.
А как заставить ноутбук автоматически включиться после продолжительного отсутствия электропитания, когда батарейка успела разрядится? В этом плане штуки вроде Raspberry выглядят надёжнее.
AX3 из коробки идут с 7-ой версией и с новой реализацией пакета для работы с WiFi (который в UI так и называется "WiFi" вместо старого "Wireless"). Поэтому на AX3 не должно быть вообще каких-то проблем, т.к. нет ни какой "миграции" при обновлении.
В вашем примере всё таки есть синхронизация серверов, хоть и медленная. Значит эту задачу можно решить с использованием грамотного UI на клиенте. Который сможет донести до юзера информацию, что его запрос всё ещё находится в обработке и надо подождать. Можно ведь рассчитать максимальное время, требуемое на синхронизацию серверов и сообщить клиенту в ответе на первый запрос, сколько времени надо что бы информация о состоянии задачи была достпна на всех серверах.
Нельзя использовать гео-балансировку по DNS? Это что за такое странное требование к гео-распределённому сервису? Ок, если на один домен - один адрес, тогда можно сделать 100500 доменов.
А что не так с тем что балансировщик знает про все шарды? Если у вас в домене только один адрес, а серверов много, то очевидно придётся делать на этом адресе балансер по всем серверам.
И ещё раз повторю - эти проблемы не имеют ни какого отношения к stateless из REST. Stateless ни помогает их решить, ни делает их решение сложнее.
Всё это имеет отношение только к тому как реализовать доступ клиента к результатам запроса, которые очень долго генерируются. И тут нет серебрянной пули. Особенно если, метафорически, запрос клиент послает в гугл, а результат ожидает от яндекса.
Так проблема в рандомной балансировке + отсутствии или очень медленной репликации данных. Надо либо сделать умный роутинг запроса, что бы он направлялся в нужный сервер, а не в рандомный (всё уже придумано до нас и есть разные варианты). Либо обеспечить быструю репликацию базы данных на все сервера, но это несколько примитивный способ, который будет в пустую тратить ресурсы.
PS: SOAP - как раз таки stateful протокол, но к данной проблеме это ни как не относится. Точно такую же проблему можно получить с чем угодно, если не поавильно спроектировать архитектуру под условия гео-распределённого сервиса.
А если пуш не дойдёт до клиента, что тогда делать? Видимо сохранить сообщение на сервере (желательно персистентно если это важное сообщение), придумать как определять, что нужный клиент вернулся и запушить ему повторно, даже если клиенту уже не нужно это сообщение. Т.е. получается то же самое только в профиль.
Пушинг или пулинг - это разные подходы для решения одной задачи. Они имеют свои плюсы и свои минусы. Пулинг проще реализовать, особенно на серверной стороне. Пушинг обеспечивает более быструю доставку информации до клиента, меньше тратит трафика, но требует более сложных решений на сервере и клиенте.
И какие ограничения в масштабировании накладывает stateless? По мне так наоборот становится проще - не надо решать пачку проблем для обеспечения работы в statefull режиме.
В stateless вообще же ничего не надо делать специального. Принял запрос, достал из него "инструкции", выполнил, вернул ответ и забыл.
"Асинхронные" запросы поверх rest ничем не отличаются от других. Мы создали какой-то ресурс в сервисе и позднее запрашиваем состояние этого ресурса. Обычная рутина для любого api.
А послать запрос и ждать ответ пока не вернёт - это вообще почти не рабочая схема в условиях работы с сетью, т.к. в любой момент связь может прерваться. А значит, что-бы это решить, придётся на клиенте запоминать какой-то уникальный идентификатор, что бы после реконекта запросить у сервера результат обработки своего запроса. Т.е. мы сделаем то же самое что и в "асинхронных" запросах.
Вы продолжаете какую-то другую задачу решать. Stateless не про то что любой сервер в кластере должен уметь обработать запрос клиента. Он только про то, что клиент должен передать в запросе всю информацию, которая нужна, что бы сервис мог обработать этот запрос.
Какой именно сервер будет обрабатывать запрос, и должн ли быть это любой сервер в кластере - это не относится к тому, что подразумевается под stateless в REST. Это другая задача, которая имеет разные решения. Начиная от полной, синхронной репликации базы продолжая шардированием с "умным" роутингом до нужной шарды и заканчивая такой реализацией где вообще не нужна база данных и любой сервер может обработать запрос клиента.
С учётом того, что репрезентация ресурса может быть совершенно разной, то я не думаю, что Филдинг закладывал в REST ограничение на то, что может возвращать и принимать сервер.
В самом класическом виде сервер может возвращать ресурс в виде html страницы, а принимать его в виде html формы. Так же ресурс может возвращаться сервером как картинка (например график), а создаваться передачей json-а со списком точек.
Это всё настолько "особенности реализации", что не стоят упоминания в фундаментальных ограничениях архитектуры (даже не приложения или протокола), которые разработал Филдинг.
Такие доп. ограничения можно вводить самостоятельно из соображений удобства разработки конкретного приложения. Например с ними проще сделать фреймворк. Но я не считаю их фундаментальными, что бы строго следовать им в любом приложении, которое претендует на тег "REST".
Я не вижу в вашей цитате Филдинга ограничение на то, что репрезентация передаваемая клиентом на сервер должна быть ограничена репрезентацией, которую возвращает сервер клиенту. Не думаю что это хоть чем-то важно в контексте REST
Вы немного про другое спрашиваете. Можно ли получить состояние от другого сервера или нет - это не имеет отношения к stateless.
Stateless исключительно про то, что клиент должен в своём запросе передать всю необходимую информацию, что бы сервер мог его обработать.
Вот пример общения которое рассчитано на наличие стейта между клиентом и сервером:
Q: Сколько продуктовых магазино в Магадане?
A: 543
Q: А сколько обувных?
A: 100
Видите где тут стейт? Второй запрос не содержит "фильтра" по городам (в Магадане). Но т.к. сервер помнит стейт, то отвечает правильно.
Сделать такое не просто, если у нас больше одного сервера. Надо уметь определять в рамках какой сессии пришёл запрос от клиента. Надо как-то хранить "сессию" и как-то удалять её когда протухла. А клиент может создать 100500 сессий. Надо сделать эту сессию доступной на других серверах или обеспечить роутинг запроса так, что бы он попадал на те сервера где есть его сессия.
Примером "ужасного" протокола с состоянием является FTP - что бы попасть в какую-то папку надо по очереди послать команды на перемещение в следующую дочернюю папку. Сервер при этом помнит где находится клиент. После этого клиент может что-то делать в папке, в которую "зашёл". Если случиться разрыв связи, то клиенту придётся опять повторять всё что он делал до этого - пересещаться постепенно в нужную папку.
Не думаю, что это определение запрещает передать, что-то дополнительное, что не является прямым отражением на поля ресурса в базе данных или ещё где хранится ресурсв. Репрезентация может иметь любой вид как по форме так и по содержанию. На то она и репрезентация сущности, а не сама сущность.
Например у ресурса может быть поле published с датой публикации. Но мы не хотим давать клиентам менять его напрямую. Для этого клиент может передавать "виртуальное" поле is_published: bool, а уже сервер будет в зависимости от него менять значение поля published.
stateles относится к тому, что клиент не должен рассчитывать на то, что сервер помнит его предыдущие запросы. Каждый новый запрос клиента должен содержать достаточно информации, что бы сервер мог его обработать. К серверу, в общем случае, это тоже относится - он не должен рассчитывать на то, что клиент помнит предыдущие ответы, он просто возвращает то что попросил клиент в текущем запросе.
Такой подход как раз позволяет упростить масштабирование сервиса, т.к. нам не надо реплицировать "знание" о предыдущих запросах на все сервера, куда может прийти запрос клиента (вы сами привели этот довод на примере сотовых станций).
Рассмотрим ваш пример в условиях гео-распределённого сервиса. Первый запрос клиента создаёт задачу, сервис возвращает URL этой задачи. После чего клиент по этому URL-у запрашивает состояние задачи. И вот тут уже видно отсутствие "состояния". Клиент не держит открытым TCP конект, через который задача была создана, что бы через этот конект потом получать состояние этой задачи. Вместо этого клиент может создать новый конект и передать туда запрос с URL-ом. И этого URL-а будет достаточно сервису, что бы понять куда в большом кластере надо направить запрос для обработки. Во первых роутинг произойдёт на уровне DNS, направив запрос в тот дата-центр, где задача была создана. Далее, балансировщик в ДЦ по URL-у определит какая "шарда" хранит в себе информацию об этой задаче и отправит запрос туда.
В результате нет ни какой необходимости делать реплики для базы данных. Можно обойтись лучше масштабируемым вариантом - шардированием.
Так же декларация использования stateless сообщает нам о том, что мы должны соответствующим образом вести разработку клиента. Клиент должен "понимать", что любой его запрос может завершиться не так как он ожидает, из-за того что сервис не гарантирует ему, что состояние его базы данных совпадает с "ожиданием" клиента. Хотя это скорее не про stateless, а общий принцип разработки мульти-пользовательских сервисов.