Вруны из Теле2, или телефонный маркетинг
Мне несколько дней названивал мой оператор, Теле2. В рабочий день телефонистка пыталась продолжить разговор, хотя я сказал, что на встрече. Пришлось бросать трубку. В выходной дозвонились и стали предлагать бесплатно перейти на специально подобранный новый тариф.
Надо сказать, что я с 2004 года использую номер на Мегафоне как основной. Подключился к Теле2 ради мобильного интернета, так как тарифы Мегафона были слишком грабительскими. Приятным бонусом оказались включенные минуты на все мобильные номера московского региона.
Телефонист начал рассказывать о новом тарифе, какой он крутой, какие в нем бесплатные звонки на междугородние номера, как много в нем мобильного интернета. После этого спрашивает: ну что, переходим прямо сейчас?
Мне неприятно принимать такие решения по телефону. Потому что надо сходу расслышать и осознать, что тебе сказали, и понять, выгодны ли тебе новые условия. А еще непонятно, каким образом устное согласие, полученное в телефонном разговоре, может иметь юридическую силу, достаточную для изменения ранее заключенного в письменном виде договора между физическим и юридическим лицом.
— Вы мне сказали, что я сейчас плачу 240 рублей в месяц, а буду 350 рублей. Почему я должен это делать?
Телефонист стал объяснять, что я тратил больше 240 рублей в последнее время
— Ладно, вы сказали, что предоставляется скидка на полгода. Что произойдет через полгода? Я стану платить в два раза больше?
— Нет, мы видим, что вы не платите столько денег, потому не заинтересованы, чтобы вы платили больше.
Я отказался, и телефонист сообщил, что в течение месяца я смогу перейти на новый тариф. После звонка пришла смс:
И тут стало всё ясно: новый тариф стоит 700, а телефонист не просто завуалировал истинную стоимость предлагаемого тарифа, а соврал на прямо поставленный вопрос.
Никогда не ведитесь на такие «индивидуальные предложения» каких бы то ни было компаний. Они никогда не будут стараться ради того, чтобы брать с вас меньше денег. Если предложение действительно интересно, возьмите паузу и внимательно прочитайте условия.
И не жалейте людей на той стороне телефона. Набирая ваш номер, они не думают о вас. Они пытаются выполнить свой план, чтобы получить зарплату и премию. И, как показывает практика, делают это не всегда честно. Такие звонки отвлекают от дел и впустую отнимают время. Не стесняйтесь отвечать «мне это не подходит» или «мне это не интересно», и класть трубку.
Кеширование и условие гонки
Одна из важных идей в программировании — кеширование. Если
Кеширование не только экономит ресурсы и делает систему более отзывчивой. Без кеширования долгих операций система в принципе не сможет работать в режиме высокой нагрузки. Если вы разрабатываете кеширование в такой системе, важно помнить об одной частой ошибке проектирования — условии гонки (race condition). Расскажу об этой проблеме на выдуманном примере главной страницы
Предположим, на главной странице
235 запросов в минуту — вроде как не очень мало. Но сайт быстро ляжет, если ссылку разместят на
Если запрос к базе данных выполнить один раз и запомнить список наиболее популярных товаров, скажем, на час, то сервер сможет выдержать до
Однако запомнить результаты выполнения запроса недостаточно. Рано или поздно они устареют, и список популярных товаров надо пересчитать заново. Здесь и кроется та самая ошибка — условие гонки. Если результаты выполнения запроса устареют сразу для всех посетителей, и этих посетителей много, тяжелый запрос начнет выполняться одновременно.
Одновременное выполнение тяжелого запроса — неприятная ситуация по многим причинам:
- Несколько конкурентных запросов могут нагружать базу данных и выполняться медленнее, чем один запрос.
- Процессы приложения вместо полезной работы будут ждать окончания выполнения долгих запросов (если, конечно, у вас не асинхронная архитектура; хотя я сомневаюсь, что в этом случае вы бы стали сейчас читать в интернете об условии гонки). При достаточном количестве посетителей 20 процессов израсходуются очень быстро, и сайт перестанет открываться, пока, наконец, не завершится выполнение долгих запросов.
- Кроме того, если вы еще и допустили ошибку при программировании самого кеша, и система записывает данные в него неатомарно (например, в файл с помощью fopen/fwrite, file_put_contents и т. д.), вы с большой вероятностью получите испорченные данные (записанные вперемешку байты из разных процессов). Если система не готова к некорректным данным в кеше, она может вообще перестать работать, пока не посчитает, что данные в кеше устарели. А если готова — продолжит пытаться выполнить тяжелый запрос в конкурентном режиме и не восстановится до тех пор, пока не посчастливится записать корректные данные в кеш, или пока не упадет нагрузка.
Как избежать условия гонки? Есть два способа.
Синхронизировать параллельные процессы. Один из процессов «прогревает кеш» (выполняет долгую операцию). Остальные понимают, что процесс прогрева идет, и всё еще используют устаревшие данные из кеша. Способ не требует глубокой переработки приложения и подходит в простых случаях. Но универсальных методов синхронизации процессов не существует. Придется подбирать подходящий: блокировка файлов (flock), блокировки в базе данных, редисе и т. д.
Прогревать кеш в фоне. Если вы хотите обновлять список популярных товаров каждый час, вычисляете его по расписанию и складываете в кеш из отдельного процесса, который не имеет отношения к обработке
Кеширование в nginx
В прошлый раз мы рассмотрели, как в теории работает кеш и какие ошибки обычно совершают при его программировании. В этот раз я расскажу, как с помощью нескольких настроек nginx включить кеширование прямо на уровне
Я уже писал о том, как работает мой сервис генерации картинок с формулами на латехе. При первом обращении к
Иногда папку с кешем приходится удалять, если она слишком сильно разрослась. Или если я вношу правки в систему, и кеш устаревает. Посетители популярных страниц генерируют множество запросов к одинаковым формулам. В кеше их нет. Nginx направляет запросы к PHP. PHP на каждую формулу вызывает консольный скрипт латеха. Раньше у меня не было защиты от того, чтобы сервер в нагруженном состоянии хотя бы не делал одно и то же много раз. Это классическое условие гонки.
Как оказалось, приемлемое решение — включить кеш в nginx и настроить блокировку. Тогда он пропускает на бэкенд разные запросы, а одинаковые выстраивает в очередь ожидания. Результат записывает в свой внутренний временный кеш и отдает всем ожидавшим клиентам.
В блоке конфигурации http указываем папку и другие параметры зоны кеша:
fastcgi_cache_path /var/data/i.upmath.me levels=1:2 keys_zone=i_upmath:10m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
Обратите внимание на ключ кеширования. Я использовал обычный адрес ресурса, потому что картинки публичны и одинаковы для всех. Если у вас закрытые ресурсы, можете попробовать добавить куки в ключ. Хотя я бы не стал так делать: велик риск ошибки и утечки чужих приватных данных через кеш.
Далее в нужном location подключаем зону:
fastcgi_cache i_upmath;
fastcgi_cache_valid 200 10m;
fastcgi_cache_methods GET HEAD;
fastcgi_cache_lock on;
fastcgi_cache_lock_age 9s;
fastcgi_cache_lock_timeout 9s;
Помимо кеширования здесь включена блокировка для предотвращения race condition. Длительность блокировки и ожидания я выбрал 9 секунд, потому что таймаут запуска латеха в моей системе 8 секунд. Вы можете подобрать другое значение.
На моем сервере ограниченное количество процессов
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
fastcgi_connect_timeout 90;
fastcgi_send_timeout 90;
Технически правильное решение в моей ситуации — прогревать новый кеш, пока система работает со старым. А именно, брать часть текущего потока запросов формул, генерировать для них новые картинки и складывать в новую папку. Когда популярные формулы окажутся в новом кеше, переключать папки.
С прогревом нового кеша пользователи не заметят подмены, и сервер будет работать в комфортном режиме. Но систему прогрева нужно еще программировать. А решение с nginx внедряется простой правкой конфига. Конечно, картинки с формулами у некоторых пользователей в момент очистки кеша перестают открываться. Но для
Одновременная вставка уникальных значений в словарные таблицы — В кресле препода №3
Как правильно добавлять данные в словарную таблицу с уникальными строками одновременно из нескольких потоков? В PostgreSQL вот так:
CREATE TABLE words (
id SERIAL PRIMARY KEY,
word TEXT NOT NULL UNIQUE
);
BEGIN;
SELECT id FROM words WHERE word = 'a';
INSERT IGNORE INTO words (word) VALUES ('a');
SELECT id FROM words WHERE word = 'a';
COMMIT;
В видео рассказываю, почему именно так, и показываю, как это работает.
00:25 Пример
01:34 Демонстрация наивной реализации вставки в словарные таблицы
02:32 Недостаток: появление дублей
03:45 Демонстрация уникального индекса
04:47 Недостаток одного только уникального индекса
05:55 Нет поддержки целостности ⇒ нужны транзакции
06:37 Демонстрация параллельной вставки в таблицу с уникальным индексом в транзакции
08:59 Вставка с игнорированием
09:17 Демонстрация вставки с игнорированием в транзакции с уровнем READ COMMITTED
12:15 Демонстрация дедлока при вставке с игнорированием в транзакции с уровнем REPEATABLE READ
13:44 Особенности метода в MySQL