сначала был Перл, где приходилось колупаться с шаблонами, чтобы вывести простенький HTML
тут появился ПХП, который позволил встраивать серверный код прямо в HTML
потом оказалось, что из этого получается говнокод, и надо использовать шаблонизатор
потом придумали на пхп делать только АПИ, а интерфейс рендерить яваскриптом - (правда, потом оказалось что на клиенте рендерить медленно, и надо сначала отправить яваскрипт на другой сервер, чтобы он там сначала отрендерился)
тут появился Fusion, которые позволяет встраивать серверный код прямо Javascript <-- вы находитесь здесь
потом окажется, что из этого получается говнокод, и надо использовать шаблонизатор для яваскрипта...
Ну вот у вас и получается в итоге "один" :) А на РНР написаны десятки.
Но вообще, мы ушли не туда. Речь не в том, "кто выше". А в том, что ваше утверждение про "простенькие сайтики" не выдерживает проверку фактами. Вы или не владеете информацией, или - что вероятнее - сознательно передёргиваете. Фу таким быть.
Ну вот зачем так откровенно передёргивать? Особенно когда вас поймают за руку раньше чем вы успеете достать краплёные карты. Весь российский екомм - от Ламоды до Буквоеда - на пыхе. И тех-эд в придачу. И без всяких страданий. Я понимаю, что вы сами верите в свою сказку про большой и могучий Руби и хилого заморыша пхп. Но против фактов со сказками сложно. Только и остаётся что рассказывать, что кто-то когда-то вам что-то сказал, или не сказал, или сказал, но не вам.
"но еще не дойдя до стадии производства цена упала до $8" - до стадии производства у вас тут доходит цена
"удобные для компоновки и составления из них практически любой логики, соединяя их выводами на передней панели и собирая модули в стойки" - здесь у вас модули соединяют сами себя.
Джонсон и в самом деле сказал, что он поехал , чтобы отклонить предложение, что ж я поделаю
Например, можно попробовать написать осмысленный текст. Как-то разделить намерение отклонить предложение и последующую работу, а не лить одним потоком сознания. А в идеале указать какую-то причину такой резкой смены намерений.
Но основная проблема - повторюсь - в том, что сам текст просто бессвязный, как на уровне отдельных предложений, так в общей логике повествования. Например, во фразе "нередко прорывало канализационные и отопительные трубы, бойлер, а порой и..." ожидается, что прорвётся что-то ещё, но речь заходит о снеге на столах. И это не какой-то исключительный пример - у вас весь текст такой. Выглядит это так, как будто вы едите, и, размахивая вилкой, одновременно торопитесь рассказать интересную историю, глотая запятые и перескакивая с места на место.
Разумеется, удалили статью с меньшим количеством минусов. Какой позор. Ну скопирую свой комментарий оттуда сюда.
Заголовок: раскрываем детали Текст: со списком моделей можно ознакомиться на сайте программы выкупа
Бесит даже не то, что текст представляет собой откровенный мусор, который 90% состоит из бормотания про рынок смартфонов и не имеет никакого отношения к заявленной теме, а то, что пиар служба компании М.Видео-Эльдорадо рассматривает Хабр как помойку для утилизации рекламной макулатуры.
К сожалению, текст практически нечитаемый. И ладно бы машинный перевод, который несёт ерунду типа "Я поехал в Мейнард, чтобы отклонить предложение" - и дальше без всякого перехода про работу в DEC.
Ладно бы постоянные шляпы, подъезжающие к станциям. Но сам текст просто бессвязный, как на уровне отдельных предложений, так в общей логике повествования. Жаль. Тема-то очень интересная.
Хорошее предложение, но ему ещё рано. Вы вспомните себя на этом этапе. Поймите, он не мучается. Он офигевает от возможности писать SQL запросы. Ему это ещё не надоело. Да и с элементарным синтаксисом SQL он ещё очень на вы. Вот когда он его освоит, а писать однообразные запросы надоест - тогда и подпихивайте ему свой ORM, и он будет его с не меньшим энтузиазмом осваивать, при этом понимая, какие запросы получаются в итоге. А сейчас если ему насильно втюхать, то получите очередную обезьяну с пишмашинкой.
Знаете, вы правы. Я действительно человек тёмный, от сохи. Не в теме про транзакции. Не читал литературу и архитектуру. Мне проще написать скрипт в 10 строчек, чем два экрана умных слов, не имеющих отношения к теме разговора.
Вот, написал. Первый
<?php
require 'pdo.php';
if (!$pdo->query("show tables like 'transtest'")->fetch()) {
$pdo->query("create table transtest (id int primary key auto_increment, user_id int, amount int)");
$pdo->query("insert into transtest values (1, 1, 100), (2, 1, -50)");
}
$pdo->query("BEGIN");
$amount = $pdo->query("select sum(amount) from transtest where user_id=1")->fetchColumn();
sleep(10);
$sum = -30;
if ($amount + $sum >= 0) {
$pdo->prepare("insert into transtest (user_id, amount) values (1, ?)")->execute([$sum]);
}
$pdo->query("COMMIT");
И второй
<?php
require 'pdo.php';
$pdo->query("BEGIN");
$amount = $pdo->query("select sum(amount) from transtest where user_id=1")->fetchColumn();
$sum = -40;
if ($amount + $sum >= 0) {
$pdo->prepare("insert into transtest (user_id, amount) values (1, ?)")->execute([$sum]);
}
$pdo->query("COMMIT");
Запускаем (php 1.php &) && php 2.php
Вторая транзакция тут не то что чтения - она даже коммита первой не ждёт.
А вы продолжайте расказывать про профайлер. Это очень, очень познавательно.
Жаль, что вы опять скатились в поучающий тон, но это было закономерно. После того, как вы не смогли объяснить, как транзакция ещё только при открытии узнаёт, что она "пишушая" и не должна давать другим транзакциям читать те данные, которые читает она (что само по себе противоречит другому вашему же утверждению) выбор у вас был невелик - или признать, что вы всё время писали ерунду, или вернуться к надуванию щёк.
При чем здесь какой-то проафйлер и кэш? Мы с вами тут говори о транзакциях. И вы явно говорили про транзакции, а не про профайлер.
Вот мы запустили базу. Вот у нас пошла первая транзакция со времени старта, по совпадению - наша транзакция по списанию денег со счёта. Как ваш профайлер понимает, что она "пишущая"?
Никак не понимает. То есть эти два ваших утверждения прямо противоречат друг другу:
При этом, сама запись остается открытой для чтения, и в случае параллельного чтения другой, не пишущей транзакцией,
Вторая транзакция начнет выполнение, только когда произойдет коммит или роллбек первой.
Вам надо сначала определиться, видит вторая транзакция данные, которые прочитала первая, или нет.
И далее, если не видит - то пояснить механизм этого "не видит". Если вторая не видит только одну строку - то почему, за счёт какого механизма БД.
В меня начинает закрадываться очень печальное подозрение, что я общаюсь с ботом.
При этом, сама запись остается открытой для чтения, и в случае параллельного чтения другой, не пишущей транзакцией,
Моя невнимательность. Я зевнул слово "не пишушей". можете объяснить, как транзакция в момент открытия понимает, пишущая она, или нет? И в целом, прошу пояснить термин "не пишущая транзакция".
В случае, если такого термина нет, то это и есть ответ на ваш вопрос.
Как тут не вспомнить анекдот про секретаршу, которая печатает те самые 10 тыщ знаков в секунду. Из того что заметил при беглом просмотре - Гек Финн явно сын негра Джима. А сам Джим превратился в карлика.
Из того, за что взгляд зацепился при беглом просмотре
функция query() - очень хорошо.
функция fetch_array() - очень так себе, пришита к классу $connect белыми нитками. И по сути это такая страусиная политика, поскольку объект mysqli_result всё равно протекает из вашей абстракции. Надо или не изображать умное лицо, а писать по-простому $result->fetch_array(), или - если делать по уму - то сделать свой класс $dbResult, который инкапсулирует работу с mysqli_result и будет логично вызываться, как $row = $dbResult->fetchArray();. Смена кейса нужна, чтобы не путать этот объект с оригинальным mysqli_result
empty($_COOKIE['tmpsid'])) OR (!isset($_COOKIE['tmpsid'])) - это масло масляное, вместе их писать никогда нет смысла. Прочтите хотя бы один раз описание empty в мануале. И в целом там какая-то дичь, почему-то условия дублируются. По уму нужно только два - isset и регулярка.
$hash = md5(ip().time()); подделывается влёт. Не надо экономить на спичках, надо сгенерить нормальное случайное значение через random_bytes() и записать его в базу
в целом лучше разделить класс Admin на два - модель и контроллер, чтобы второй в своей работе использовал методы первого, а не работал с базой напрямую.
"Все наименование полей и таблиц собираются из формы, а так же: тип данных, поле с уникальным значеним, обязательно поле к заполнению" - такой GraphQL на коленке, наверняка состоит из инъекций чуть более, чем весь. В итоге размазали работу БД между моделью и клиентом. Нормальный такой MVC.
В целом, как я и писал, это материал для Тостера, а не для Хабра. Если в с первой статьёй вас решили поддержать, то эта вряд ли будет в плюсах. С другой стороны, фидбека вы соберёте полную панамку.
Первая транзакция прочитала сумму, и убедилась, что списание не выходит из лимита. Вторая транзакция по вашему сценарию прочитала сумму, и убедилась, что списание не выходит из лимита. Первая транзакция добавила строку и закоммитилась. Вторая транзакция добавила строку и закоммитилась. Сумма ушла в минус.
Денис, вы меня извините, но подход "отбраковка руками пользователей" для меня остался в прошлом веке, вместе с компанией "Рога и Копыта" с Большой Якиманки.
В этом веке я ожидаю, что ляпы будут единичными, а не мейнстримом. А от новостного редактора я ожидаю, что он хотя бы один раз прочтёт своё творение и попытается понять его смысл. А не будет ждать, когда его натыкают носом читатели.
Причём речь идёт не об отдельных ляпах типа опечаток или несогласованных предложений, а о целых абзацах или даже постах, таких как например ваше предыдущее творение, в котором 150 лет лёгким движением руки превращаются в 300.
При этом, сама запись остается открытой для чтения, и в случае параллельного чтения другой, не пишущей транзакцией, в случае отката нашей, та вернет актуальные данные!
Ну так об этом и речь. А в случае успешного завершения транзакции прочитанное значение станет невалидным, и позволит выйти из лимита.
А предотвратить это можно только блокировкой на чтение. Причём у нас только два варианта - либо лочить только записи пользователя, что, по вашему "не нужно" - и тогда остаётся лочить всю таблицу. Уровнем ли изоляции, явной ли блокировкой - но суть одна.
Нам нужна очередь из транзакций с insert.
Опять у вас общие фразы, не имеющие ничего общего с реальностью. У нас даже близко нет ничего похожего на "просто очередь транзакций с insert". У нас очередь запросов insert с условием. И состояние гонки. Напоминаю - это не просто "хронологически связанные данные", которые льются потоком. А данные, которые добавляются или не добавляются в зависимости от текущего состояния БД.
Опять же, в порядке конструктивного диалога с самим собой: в теории, наверное можно делать и по вашему принципу: валить всё, что пришло, а после вставки считать результат, и если меньше лимита, то откатывать вставку. Но этот вариант мне нравится меньше всех остальных. Да, вероятность получить пополнение между снятиями практически нулевая, но в жизни всякое бывает. И, главное, такой подход исходно выглядит недетерминированным, то есть могут быть и другие проблемы, которых я сейчас не могу сообразить.
Прямо целый инфоцыганский табор.
Ну как же!
сначала был Перл, где приходилось колупаться с шаблонами, чтобы вывести простенький HTML
тут появился ПХП, который позволил встраивать серверный код прямо в HTML
потом оказалось, что из этого получается говнокод, и надо использовать шаблонизатор
потом придумали на пхп делать только АПИ, а интерфейс рендерить яваскриптом
- (правда, потом оказалось что на клиенте рендерить медленно, и надо сначала отправить яваскрипт на другой сервер, чтобы он там сначала отрендерился)
тут появился Fusion, которые позволяет встраивать серверный код прямо Javascript <-- вы находитесь здесь
потом окажется, что из этого получается говнокод, и надо использовать шаблонизатор для яваскрипта...
Ну вот у вас и получается в итоге "один" :)
А на РНР написаны десятки.
Но вообще, мы ушли не туда. Речь не в том, "кто выше". А в том, что ваше утверждение про "простенькие сайтики" не выдерживает проверку фактами. Вы или не владеете информацией, или - что вероятнее - сознательно передёргиваете. Фу таким быть.
Скажите, вам самому не смешно это писать? :) Вот именно так, не "написаны на Руби", а "Руби там где-то есть"? :-D
Ну вот зачем так откровенно передёргивать? Особенно когда вас поймают за руку раньше чем вы успеете достать краплёные карты. Весь российский екомм - от Ламоды до Буквоеда - на пыхе. И тех-эд в придачу. И без всяких страданий. Я понимаю, что вы сами верите в свою сказку про большой и могучий Руби и хилого заморыша пхп. Но против фактов со сказками сложно. Только и остаётся что рассказывать, что кто-то когда-то вам что-то сказал, или не сказал, или сказал, но не вам.
Это был просто баг браузера. Сейчас всё исправили и уже нагнали плюсов, как и положено :)
«Подъезжая к станции, у меня слетела шляпа».
"но еще не дойдя до стадии производства цена упала до $8" - до стадии производства у вас тут доходит цена
"удобные для компоновки и составления из них практически любой логики, соединяя их выводами на передней панели и собирая модули в стойки" - здесь у вас модули соединяют сами себя.
Например, можно попробовать написать осмысленный текст. Как-то разделить намерение отклонить предложение и последующую работу, а не лить одним потоком сознания. А в идеале указать какую-то причину такой резкой смены намерений.
Но основная проблема - повторюсь - в том, что сам текст просто бессвязный, как на уровне отдельных предложений, так в общей логике повествования. Например, во фразе "нередко прорывало канализационные и отопительные трубы, бойлер, а порой и..." ожидается, что прорвётся что-то ещё, но речь заходит о снеге на столах. И это не какой-то исключительный пример - у вас весь текст такой. Выглядит это так, как будто вы едите, и, размахивая вилкой, одновременно торопитесь рассказать интересную историю, глотая запятые и перескакивая с места на место.
Разумеется, удалили статью с меньшим количеством минусов. Какой позор. Ну скопирую свой комментарий оттуда сюда.
Заголовок: раскрываем детали
Текст: со списком моделей можно ознакомиться на сайте программы выкупа
Бесит даже не то, что текст представляет собой откровенный мусор, который 90% состоит из бормотания про рынок смартфонов и не имеет никакого отношения к заявленной теме, а то, что пиар служба компании М.Видео-Эльдорадо рассматривает Хабр как помойку для утилизации рекламной макулатуры.
К сожалению, текст практически нечитаемый. И ладно бы машинный перевод, который несёт ерунду типа "Я поехал в Мейнард, чтобы отклонить предложение" - и дальше без всякого перехода про работу в DEC.
Ладно бы постоянные шляпы, подъезжающие к станциям. Но сам текст просто бессвязный, как на уровне отдельных предложений, так в общей логике повествования. Жаль. Тема-то очень интересная.
Хорошее предложение, но ему ещё рано. Вы вспомните себя на этом этапе. Поймите, он не мучается. Он офигевает от возможности писать SQL запросы. Ему это ещё не надоело. Да и с элементарным синтаксисом SQL он ещё очень на вы. Вот когда он его освоит, а писать однообразные запросы надоест - тогда и подпихивайте ему свой ORM, и он будет его с не меньшим энтузиазмом осваивать, при этом понимая, какие запросы получаются в итоге. А сейчас если ему насильно втюхать, то получите очередную обезьяну с пишмашинкой.
Знаете, вы правы. Я действительно человек тёмный, от сохи. Не в теме про транзакции. Не читал литературу и архитектуру. Мне проще написать скрипт в 10 строчек, чем два экрана умных слов, не имеющих отношения к теме разговора.
Вот, написал. Первый
И второй
Запускаем
(php 1.php &) && php 2.php
Вторая транзакция тут не то что чтения - она даже коммита первой не ждёт.
А вы продолжайте расказывать про профайлер. Это очень, очень познавательно.
Жаль, что вы опять скатились в поучающий тон, но это было закономерно. После того, как вы не смогли объяснить, как транзакция ещё только при открытии узнаёт, что она "пишушая" и не должна давать другим транзакциям читать те данные, которые читает она (что само по себе противоречит другому вашему же утверждению) выбор у вас был невелик - или признать, что вы всё время писали ерунду, или вернуться к надуванию щёк.
При чем здесь какой-то проафйлер и кэш? Мы с вами тут говори о транзакциях. И вы явно говорили про транзакции, а не про профайлер.
Вот мы запустили базу.
Вот у нас пошла первая транзакция со времени старта, по совпадению - наша транзакция по списанию денег со счёта. Как ваш профайлер понимает, что она "пишущая"?
Никак не понимает. То есть эти два ваших утверждения прямо противоречат друг другу:
Вам надо сначала определиться, видит вторая транзакция данные, которые прочитала первая, или нет.
И далее, если не видит - то пояснить механизм этого "не видит". Если вторая не видит только одну строку - то почему, за счёт какого механизма БД.
В меня начинает закрадываться очень печальное подозрение, что я общаюсь с ботом.
Моя невнимательность. Я зевнул слово "не пишушей". можете объяснить, как транзакция в момент открытия понимает, пишущая она, или нет? И в целом, прошу пояснить термин "не пишущая транзакция".
В случае, если такого термина нет, то это и есть ответ на ваш вопрос.
Как тут не вспомнить анекдот про секретаршу, которая печатает те самые 10 тыщ знаков в секунду. Из того что заметил при беглом просмотре - Гек Финн явно сын негра Джима. А сам Джим превратился в карлика.
Вcё верно, хорошая реклама. Прямо становление программиста в прямом эфире :)
Со временем дойдёт и до всех этих ваших страшных слов. Перепрыгнуть всё равно не получится.
Из того, за что взгляд зацепился при беглом просмотре
функция query() - очень хорошо.
функция fetch_array() - очень так себе, пришита к классу $connect белыми нитками. И по сути это такая страусиная политика, поскольку объект mysqli_result всё равно протекает из вашей абстракции. Надо или не изображать умное лицо, а писать по-простому $result->fetch_array(), или - если делать по уму - то сделать свой класс $dbResult, который инкапсулирует работу с mysqli_result и будет логично вызываться, как $row = $dbResult->fetchArray();. Смена кейса нужна, чтобы не путать этот объект с оригинальным mysqli_result
empty($_COOKIE['tmpsid'])) OR (!isset($_COOKIE['tmpsid']))
- это масло масляное, вместе их писать никогда нет смысла. Прочтите хотя бы один раз описание empty в мануале. И в целом там какая-то дичь, почему-то условия дублируются. По уму нужно только два - isset и регулярка.$hash = md5(ip().time()); подделывается влёт. Не надо экономить на спичках, надо сгенерить нормальное случайное значение через random_bytes() и записать его в базу
в целом лучше разделить класс Admin на два - модель и контроллер, чтобы второй в своей работе использовал методы первого, а не работал с базой напрямую.
"Все наименование полей и таблиц собираются из формы, а так же: тип данных, поле с уникальным значеним, обязательно поле к заполнению" - такой GraphQL на коленке, наверняка состоит из инъекций чуть более, чем весь. В итоге размазали работу БД между моделью и клиентом. Нормальный такой MVC.
В целом, как я и писал, это материал для Тостера, а не для Хабра. Если в с первой статьёй вас решили поддержать, то эта вряд ли будет в плюсах. С другой стороны, фидбека вы соберёте полную панамку.
А зачем здесь параллельное-то?
Первая транзакция прочитала сумму, и убедилась, что списание не выходит из лимита.
Вторая транзакция по вашему сценарию прочитала сумму, и убедилась, что списание не выходит из лимита.
Первая транзакция добавила строку и закоммитилась.
Вторая транзакция добавила строку и закоммитилась.
Сумма ушла в минус.
Всё строго последовательно.
Денис, вы меня извините, но подход "отбраковка руками пользователей" для меня остался в прошлом веке, вместе с компанией "Рога и Копыта" с Большой Якиманки.
В этом веке я ожидаю, что ляпы будут единичными, а не мейнстримом. А от новостного редактора я ожидаю, что он хотя бы один раз прочтёт своё творение и попытается понять его смысл. А не будет ждать, когда его натыкают носом читатели.
Причём речь идёт не об отдельных ляпах типа опечаток или несогласованных предложений, а о целых абзацах или даже постах, таких как например ваше предыдущее творение, в котором 150 лет лёгким движением руки превращаются в 300.
Вот! Теперь есть
к чему придратьсяконкретика.Ну так об этом и речь. А в случае успешного завершения транзакции прочитанное значение станет невалидным, и позволит выйти из лимита.
А предотвратить это можно только блокировкой на чтение. Причём у нас только два варианта - либо лочить только записи пользователя, что, по вашему "не нужно" - и тогда остаётся лочить всю таблицу. Уровнем ли изоляции, явной ли блокировкой - но суть одна.
Опять у вас общие фразы, не имеющие ничего общего с реальностью. У нас даже близко нет ничего похожего на "просто очередь транзакций с insert". У нас очередь запросов insert с условием. И состояние гонки. Напоминаю - это не просто "хронологически связанные данные", которые льются потоком. А данные, которые добавляются или не добавляются в зависимости от текущего состояния БД.
Опять же, в порядке конструктивного диалога с самим собой: в теории, наверное можно делать и по вашему принципу: валить всё, что пришло, а после вставки считать результат, и если меньше лимита, то откатывать вставку. Но этот вариант мне нравится меньше всех остальных. Да, вероятность получить пополнение между снятиями практически нулевая, но в жизни всякое бывает. И, главное, такой подход исходно выглядит недетерминированным, то есть могут быть и другие проблемы, которых я сейчас не могу сообразить.