<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>oid.su</title>
      <link>https://qqq.oid.su</link>
      <description>Заметки</description>
      <generator>Zola</generator>
      <language>ru</language>
      <atom:link href="https://qqq.oid.su/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Tue, 12 May 2026 00:00:00 +0000</lastBuildDate>
      <item>
          <title>SQLite в проде, год спустя</title>
          <pubDate>Tue, 12 May 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://qqq.oid.su/blog/sqlite-v-prode/</link>
          <guid>https://qqq.oid.su/blog/sqlite-v-prode/</guid>
          <description xml:base="https://qqq.oid.su/blog/sqlite-v-prode/">&lt;p&gt;Год назад я выкинул Postgres из своего пет-проекта и поставил один файл &lt;code&gt;.db&lt;&#x2F;code&gt;
рядом с бинарником. С тех пор база пережила два переезда, четыре релиза и одно
бухое воскресенье. Делюсь что выяснилось.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;chto-ia-ozhidal-poluchit&quot;&gt;Что я ожидал получить&lt;&#x2F;h2&gt;
&lt;p&gt;Ноль операционной нагрузки. Никаких &lt;em&gt;RDS&lt;&#x2F;em&gt;, никаких &lt;em&gt;pg_hba.conf&lt;&#x2F;em&gt;, никаких
странных таймаутов в три ночи. Бэкап — это &lt;code&gt;cp&lt;&#x2F;code&gt;. Миграция — это &lt;code&gt;scp&lt;&#x2F;code&gt;.
Восстановление — это уже выученный наизусть пароль от ssh.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;chto-poluchil-na-samom-dele&quot;&gt;Что получил на самом деле&lt;&#x2F;h2&gt;
&lt;p&gt;Всё то же самое, плюс одна неожиданная штука: я наконец начал думать о схеме
данных &lt;em&gt;до&lt;&#x2F;em&gt;, а не &lt;em&gt;после&lt;&#x2F;em&gt;. Когда база — это файл, который ты можешь скачать и
открыть в DB Browser за две секунды, ленивые миграции «потом починим» внезапно
становятся непростительно ленивыми. Файл лежит перед тобой целиком, врать ему
некуда.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gde-vsio-taki-prizhalo&quot;&gt;Где всё-таки прижало&lt;&#x2F;h2&gt;
&lt;p&gt;Записей было немного, но они шли пачками. По дефолту SQLite отдаёт &lt;code&gt;SQLITE_BUSY&lt;&#x2F;code&gt;,
как только два писателя сталкиваются лбами. Лечится одной строчкой:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;sql&quot;&gt;PRAGMA journal_mode = WAL;
PRAGMA busy_timeout = 5000;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;WAL разводит читателей и писателя по разным углам, а &lt;code&gt;busy_timeout&lt;&#x2F;code&gt; говорит
драйверу не паниковать, а подождать. После этого «конкурентность» в моём
масштабе (десятки запросов в секунду, не тысячи) перестала быть темой для
разговора вообще.&lt;&#x2F;p&gt;
&lt;p&gt;Второе, что укусило, — это типизация. SQLite хранит что положишь, а не что
объявил. Строка в колонке &lt;code&gt;INTEGER&lt;&#x2F;code&gt;? Пожалуйста. Спасает либо строгий режим
(&lt;code&gt;STRICT&lt;&#x2F;code&gt; таблицы), либо дисциплина на уровне приложения. Я выбрал &lt;code&gt;STRICT&lt;&#x2F;code&gt; и ни
разу не пожалел.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bekap-kotoryi-ne-vriot&quot;&gt;Бэкап, который не врёт&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;cp&lt;&#x2F;code&gt; живого файла под нагрузкой — это лотерея. Правильный способ ровно один:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;sh&quot;&gt;sqlite3 app.db &amp;quot;.backup &amp;#39;&#x2F;backups&#x2F;app-$(date +%F).db&amp;#39;&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Эта команда снимает консистентный снапшот, не блокируя писателей надолго.
Кладёшь в cron, рядом — &lt;code&gt;restic&lt;&#x2F;code&gt; в S3-совместимое хранилище, и спишь спокойно.
Весь «бэкап-пайплайн» — пять строк в скрипте.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;itogo&quot;&gt;Итого&lt;&#x2F;h2&gt;
&lt;p&gt;SQLite — это не «база для бедных». Это осознанный выбор в пользу простоты, когда
ты честно понимаешь свой профиль нагрузки. Один файл, ноль демонов, бэкап
копированием. Postgres вернётся в мою жизнь в тот день, когда мне реально
понадобится то, что умеет только он. Пока — не понадобился.&lt;&#x2F;p&gt;
</description>
      </item>
    </channel>
</rss>
