Settings¶
Everything lives under TELEGRAM_BOT in settings.py. Scalar values can also
come from DJANGO_AIOGRAM_<NAME>; Django settings take precedence.
All of it is validated by manage.py check — in processes where the bot is
enabled or the event log is on. A container with ENABLED=0 and the log
recording still registers every rule, including the ones about the bot's own
settings. The credential warnings stay silent there — W001 and W002 are gated on
the bot being enabled, as the table below says — but the log's own rules are not:
measured on {'ENABLED': False, 'EVENT_LOG': True}, that process reports W005,
W006 and I001. Plain manage.py check exits 0 on all three, and the
--fail-level WARNING this documentation recommends for CI fails on the two
warnings. Only a process with ENABLED and EVENT_LOG both switched off registers
nothing.
E047 is split along the same line, which matters to a base install: since 4.0 no
transport driver is a dependency of this package, and a process that reaches no
transport is not asked to install one. Measured on a disabled process installed
without django-aiogram[redis]: no E047, and manage.py check still exits 0. A
BROKER naming something that is not a transport is still reported there, because
that name is equally wrong in the worker that does send.
Credentials¶
| Setting | Default | Description |
|---|---|---|
TOKEN |
'' |
Telegram bot token |
REDIS_URL |
'' |
Redis connection URL, including the database index |
Neither is required for the project to boot.
Which processes run the bot¶
| Setting | Default | Description |
|---|---|---|
ENABLED |
True |
Whether this process sends — to Telegram, or into the broker. The depth reads answer regardless |
AUTODISCOVER |
True |
Import <app>.<MODULE_NAME> on startup |
MODULE_NAME |
'tg_router' |
Module to look for in each installed app |
Every boolean setting here is parsed, not tested for truthiness: 'false',
'no', 'off' and 0 all mean false, wherever a boolean is accepted. Anything
unparseable raises ImproperlyConfigured rather than being read as true — which
matters because the environment can only give you a string, so 'false' under a
bare truthiness test would mean the opposite of what it says. RAISE_EXCEPTION
was the last setting read that way, and no longer is. See Deployment.
Bot behavior¶
| Setting | Default | Description |
|---|---|---|
DEFAULT_BOT_PROPERTIES |
{} |
Passed to aiogram's DefaultBotProperties |
DEFAULT_KWARGS |
lambda fn: {} |
Per-function extras the above cannot express |
FSM_STORAGE |
'redis' |
'redis', 'memory', or a dotted path |
MAX_RETRIES |
10 |
Retries after a Telegram rate-limit refusal |
RAISE_EXCEPTION |
False |
Let send_raw propagate failures |
TRANSACTIONAL |
False |
Hold the queue write until the caller's transaction commits |
TRANSACTIONAL is about a send made inside transaction.atomic(). With it off — which
is what every release before 4.1 did, and what an upgrade keeps doing — the write reaches
the broker immediately, and a block that raises afterwards leaves the message sent and the
row it announced rolled back. With it on, the write waits for the commit on the default
connection and a rolled-back block queues nothing at all.
What it does not change, and what to expect:
- Outside a transaction nothing moves. A send with no
atomic()around it publishes where it stands, on either setting. - The correlation id is the same value either way. It is resolved before the write is
attempted, not derived from it, so a caller storing it beside its own row is unaffected —
and it is the deferred call that returns sooner, because the immediate one waits for the
broker to acknowledge the write before
send()returns at all. - A broadcast holds its payloads until the commit, and only the immediate path is
chunk-bounded.
send_manyserializes a chunk at a time and publishes it before preparing the next, so peak memory is one chunk — with the write deferred there is nothing to publish yet, and every payload waits in memory until the block ends. Fifty thousand chats inside a long transaction is the shape to avoid. One commit hook is registered per call, so a chunk the broker refuses stops the ones behind it, exactly as the immediate path does. - A send never opens a database connection. The
defaultconnection is inspected only when it is already established: a process that touches no database keeps not touching one, and a connection nothing has run on cannot be inside a transaction anyway. - The
outbound.queuedrow waits too, and carries the time of the call rather than of the commit — a message deferred for the length of a transaction reads as that much queue latency. Nothing is recorded for a block that rolled back, because nothing was queued. - A payload that cannot be serialized still raises at the call. Only the write waits.
- The async twins never defer. Django has no asynchronous transactions, and a coroutine
holds its own database connection rather than the one a surrounding block opened — so
await bot.asend()has no commit to wait for and publishes where it stands. - A publish that fails after the commit cannot undo it. The drop is recorded, and
RAISE_EXCEPTIONdecides whether the exception also leaves theatomic()block — where it would take any commit hook queued behind this one with it. AUTOCOMMIT: Falseon the alias is not supported by this, with or without anatomic()block inside it. Without one, Django'son_commitrefuses a manually managed transaction outright; with one, it accepts the hook and then runs it onset_autocommit(True)rather than when the block ends — a moment no caller chose, and one a process that keeps autocommit off never reaches. Both publish immediately instead, and a line in the log says so once per process.- A scheduled send's
outbound.scheduledevent waits on a weaker condition than this, and deliberately: it is durable, the schedule row it describes is the caller's own write, and nothing would ever take the event back if the block rolled that row away. Arriving onset_autocommit(True)is a real cost for a message and none at all for a record, so the event takes the deferral this setting refuses — and it has no unsupported configuration of its own, because a caller that leaves no block for a hook to live in gets one fromscheduleitself. This bullet is about the message; the event is safe on every alias. - Inside the bot container it does nothing. A send there reaches Telegram directly and there is no queue write to hold back.
DEFAULT_BOT_PROPERTIES accepts every field aiogram defines: parse_mode,
disable_notification, protect_content, allow_sending_without_reply,
link_preview, link_preview_is_disabled, link_preview_prefer_small_media,
link_preview_prefer_large_media, link_preview_show_above_text,
show_caption_above_media. A misspelling fails at manage.py check.
TELEGRAM_BOT = {
'DEFAULT_BOT_PROPERTIES': {
'parse_mode': 'HTML',
'link_preview_is_disabled': True,
},
}
DEFAULT_KWARGS covers what bot properties cannot, such as a default caption:
def default_kwargs(function: str) -> dict:
return {'send_photo': {'caption': 'Photo'}}.get(function, {})
Updates¶
These decide how updates reach the bot. They have nothing to do with the queue, which carries outbound messages in both modes — see Webhook.
| Setting | Default | Description |
|---|---|---|
MODE |
'polling' |
Where updates come from: 'polling' or 'webhook' |
WEBHOOK_URL |
'' |
Where Telegram posts updates; required when MODE is 'webhook' |
WEBHOOK_SECRET |
'' |
Required with WEBHOOK_URL; the view compares it with the header Telegram echoes |
WEBHOOK_ALLOWED_UPDATES |
() |
Update types to receive; empty means Telegram's default set |
Queue¶
| Setting | Default | Description |
|---|---|---|
DELIVERY |
'django_aiogram.consumer.delivery.BlpopDelivery' |
Dotted path to the consumer class. Your own Delivery subclass goes here — see Delivery |
BROKER |
'django_aiogram.broker.redis_list.RedisListBroker' |
Which transport carries messages, by dotted path. Nothing is inferred from what happens to be installed: a name whose driver is missing is a system check with the pip install line, not an ImportError on the first send. Each broker declares its own settings — see the table below — and the other queue rows here belong to the package rather than to a transport. A broker with no sensible default for where a message goes marks that setting required and refuses at startup without it |
WORKER_NAME |
hostname | Names this worker's in-flight list on the Redis list transport, and nothing on the other three — see Redis list |
BLPOP_TIMEOUT |
5 |
How often the blocking take is interrupted to check for shutdown, capped at min(HEARTBEAT_INTERVAL, floor(<the transport timeout>) - 1), never below 1 — the transport's timeout being REDIS_TIMEOUT, RABBITMQ_TIMEOUT or KAFKA_TIMEOUT, whichever BROKER names. Floored because two of those accept fractions, so a 2.6 deadline leaves a whole second inside it rather than 1.6. W004 says so and names the bound that binds. The package's, not a transport's: every consumer reads it, whichever queue it takes from |
DRAIN_TIMEOUT |
5 |
Seconds close() gives in-flight sends to finish before canceling them |
MAX_IN_FLIGHT |
0 |
Sends the consumer leaves in flight before it stops taking messages; 0 is no bound |
REQUIRE_CRASH_SAFE |
False |
Refuse to start where a message cannot survive the worker being killed mid-send |
REDIS_TIMEOUT |
10 |
Seconds a single Redis call may take before the server counts as gone |
HEARTBEAT_INTERVAL |
10 |
Seconds between the consumer's reports; the key lives three times as long, which is also the most --max-age can observe |
HEALTHCHECK_MAX_QUEUE |
0 |
Longest queue still considered healthy; the check fails only above it, and 0 disables it |
SERIALIZER |
'json' |
'json' or 'pickle' — see Serialization |
ALLOW_PICKLE |
False |
Let the reader accept pickled payloads. Needed to read them at all, and needed alongside SERIALIZER: 'pickle' to write them. Unpickling queue data is code execution, so only on a queue nothing untrusted can write to |
What each transport declares¶
BROKER names one of these, and only that one's settings are read. A setting belonging to a
transport you are not using is reported by W003 as a key nothing reads, which is what it is.
| Transport | Settings | Required |
|---|---|---|
django_aiogram.broker.redis_list.RedisListBroker |
REDIS_URL, REDIS_MESSAGES_KEY, REDIS_TIMEOUT |
none — the list has a default name |
django_aiogram.broker.redis_streams.RedisStreamsBroker |
REDIS_URL, REDIS_STREAM_KEY, REDIS_STREAM_GROUP, REDIS_TIMEOUT |
REDIS_STREAM_KEY |
django_aiogram.broker.rabbitmq.RabbitMQBroker |
RABBITMQ_URL, RABBITMQ_QUEUE, RABBITMQ_PREFETCH, RABBITMQ_TIMEOUT |
RABBITMQ_URL, RABBITMQ_QUEUE |
django_aiogram.broker.kafka.KafkaBroker |
KAFKA_BOOTSTRAP, KAFKA_TOPIC, KAFKA_GROUP, KAFKA_TIMEOUT |
KAFKA_BOOTSTRAP, KAFKA_TOPIC |
Redis list¶
The Redis list declares three settings, gathered here because the other three transports have their own sections.
Two of them are in the package-wide tables above as well, and mean it: REDIS_URL under
Credentials and REDIS_TIMEOUT under Queue are read by the package under every
transport, because the FSM storage builds a Redis client whatever carries the messages. A project
running Kafka with FSM_STORAGE = 'redis' sets both and neither is stranded. REDIS_MESSAGES_KEY
is not like that — a list key is read by nothing under Kafka — so it lives here alone, W003
reports it where another transport is configured, and E007 validates it only where this one is.
| Setting | Default | Description |
|---|---|---|
REDIS_URL |
'' |
Where the server is. Shared with the Streams transport and with the FSM storage, which is why it also sits under Credentials above |
REDIS_MESSAGES_KEY |
'TELEGRAM_BOT_MESSAGE' |
The list, and the prefix the in-flight and heartbeat keys derive from |
REDIS_TIMEOUT |
10 |
The deadline on any single call. E030 refuses anything below 2, because the pop has to sit a second inside it |
Redis Streams¶
| Setting | Default | Description |
|---|---|---|
REDIS_STREAM_KEY |
required | The stream XADD writes to. No default on purpose: one would sit a suffix away from REDIS_MESSAGES_KEY, and XADD against a key holding a list answers WRONGTYPE on the first send rather than at startup. E047 asks for it before anything runs |
REDIS_STREAM_GROUP |
'django-aiogram' |
The consumer group every worker joins, so they share the stream instead of each reading all of it. Defaulted, because unlike the key there is nothing another transport could collide with |
RabbitMQ¶
| Setting | Default | Description |
|---|---|---|
RABBITMQ_URL |
required | amqp://user:pass@host:5672/vhost. No default: it carries credentials and a host, and neither is worth baking in |
RABBITMQ_QUEUE |
required | The durable queue messages are published to and consumed from. No default, for the reason REDIS_STREAM_KEY has none — where messages go is not something to guess |
RABBITMQ_PREFETCH |
0 |
basic_qos prefetch, where 0 means the broker does not limit how many unacknowledged messages a consumer may hold. Left unlimited on purpose: MAX_IN_FLIGHT already bounds that, and a prefetch below it would stall the consumer — it would hold its limit of unacknowledged sends and be given nothing more until one finished |
RABBITMQ_TIMEOUT |
10 |
Seconds a call may sit on a blocked connection before it gives up. RabbitMQ blocks a publisher's connection under memory or disk pressure, and pika leaves this unset — measured — so a blocked connection would hold every synchronous call on it for ever, and publish runs on request threads. This wins over a blocked_connection_timeout written into RABBITMQ_URL, which it did not before 4.0: the consumer's take is capped by this number and the shutdown join is derived from it, so a URL overriding it left both describing a deadline no call carried. Every other pika parameter in the URL is still yours — socket_timeout included, which does not bound a take. Has to be a positive finite number, and E047 refuses anything else by name — a 0 here used to reach pika as 10 while W004 and the consumer's cap read it as 0 |
A publish here is confirmed and mandatory: the broker answers before send() returns, and
a message that cannot be routed raises instead of vanishing into an exchange. That matches what
the Redis transports already do — RPUSH answers with the new length — and it costs what the
guarantee costs: measured, 323–393µs against 15–20µs for the same publish with only the confirm
taken off. Most of that is the disk rather than the round trip — the same publish without
persistence is 135–173µs. A Redis list publish, measured the same way and on the same machine, is
120–147µs.
Those two are not divided here, and that is deliberate: they come from different scripts run at different times, so there is no pair of numbers from one run to divide. What they support is an ordering — this is the most expensive publish of the four, above Kafka's 166–295µs — and the ordering is also the only part that survives a change of footing. A native Redis publish was measured at 14–19µs in 3.1.0, and a multiple against that baseline would read quite differently from one against this.
Nothing here needs WORKER_NAME. An unacknowledged message returns to the queue when the
channel that held it drops, which is what a worker being killed does to it — so there is no
in-flight list, reclaim() has nothing to do, tgbot_reclaim refuses, and I001 stays quiet.
The healthcheck reports the consumer as not observable from outside, because RabbitMQ tracks
its own consumers and this package writes nothing about them.
Redis 7.0 or newer, for this transport only — the package floor stays 6.2 for the list.
XINFO GROUPS grew the lag field in 7.0 and it is the only exact answer to how many
messages are waiting; measured, 6.2 has no such field, and Redis has no command that counts a
range. The broker probes for the field on first use and refuses by name without it rather than
reporting a number that would drive HEALTHCHECK_MAX_QUEUE wrongly.
Never trim this stream by length. XADD MAXLEN and XDEL remove exactly the entries an
unfinished send leaves unacknowledged. Measured: trim past a pending entry and XPENDING
still reports it while XAUTOCLAIM hands the id back in its deleted list — the message is
gone and no consumer can replay it. XDEL additionally costs Redis the ability to answer
lag at all — a depth read then refuses instead of guessing. That one is temporary:
measured, the count returns once the group has read through to the end of the stream, so it
is unavailable exactly while there is a backlog, and XSETID restores it at once. The broker's own trim() stops at the oldest unacknowledged entry.
Kafka¶
| Setting | Default | Description |
|---|---|---|
KAFKA_BOOTSTRAP |
required | host:port[,host:port…]. No default, for the reason the AMQP URL has none |
KAFKA_TOPIC |
required | The topic messages are produced to and consumed from |
KAFKA_GROUP |
'django-aiogram' |
The consumer group every worker joins, so they share the partitions instead of each reading all of them |
KAFKA_TIMEOUT |
10 |
Seconds any single call may take — the socket timeout, and how long a publish waits for the broker's acknowledgement before it reports a refusal. Between 0.01 and 300, which is what librdkafka accepts for the setting this becomes; outside that it is refused at startup rather than by the driver |
Kafka settles a position, not a message, and that is the difference to understand before
choosing it. Committing offset N says every message below N has been dealt with. So a consumer
holding several sends at once — which MAX_IN_FLIGHT allows — cannot settle them in whatever
order they finish: this broker commits the highest contiguous prefix and holds anything
settled out of order until the gap below it closes. Nothing is lost, and a burst of slow sends
delays the commit rather than skipping it.
The same shape has a sharper edge on release. There is no per-message nack in Kafka, so
giving a message up means rewinding to its offset — and that record, together with every later
one in its partition, is delivered again. Build idempotency on your own business key, which
the delivery page recommends generally and which matters most here.
A publish waits for the broker — 166 to 295µs for one message, across eleven runs. On the
same footing a Redis list publish is 120–147µs and RabbitMQ's confirmed one 323–393, which puts
this between them; the numbers are not divided against each other, for the reason the AMQP
section above gives. produce() itself answers in 0.2µs because librdkafka's own thread does the
I/O, and returning there would be a weaker promise than RPUSH already makes. Automatic topic
creation is the broker's setting, not this package's: with it off, a missing topic is a refusal at
publish time.
Nothing here needs WORKER_NAME. A consumer that dies stops heartbeating, the group
rebalances, and its partitions go to another member from the last committed offset — so
reclaim() has nothing to do, tgbot_reclaim refuses, and the healthcheck reports the
consumer as not observable from outside.
Rate limits¶
| Setting | Default | Description |
|---|---|---|
RATE_LIMIT |
see below | Proactive pacing, or None to disable |
TELEGRAM_BOT = {
'RATE_LIMIT': {
'overall_per_second': 30,
'per_chat_per_second': 1,
'group_per_minute': 20,
},
}
See Rate limits.
Event log¶
Off by default. Turning it on records what the bot did into one append-only
table, which needs manage.py migrate and a retention job — see
Event log before you enable it.
| Setting | Default | Description |
|---|---|---|
EVENT_LOG |
False |
Record events at all. Gates both the writing and the admin |
EVENT_LOG_KINDS |
() |
Which kinds to keep; empty means every kind this version knows. Naming any also opts out of the kinds a later release adds, and filters events_recorded receivers as well as rows. log.dropped is exempt either way — it is the record that recording fell behind. A set that leaves out any of the four kinds a correct outcome requires also switches off bot.outcome(), and the call refuses rather than answering unknown for ever — Event log has which four and why |
EVENT_LOG_PAYLOAD |
'summary' |
'none', 'summary' (argument names and sizes) or 'full' (message bodies) |
EVENT_LOG_MAX_PAYLOAD_BYTES |
8192 |
Cap on the JSON column; 0 stores no payload at all |
EVENT_LOG_REDACT_KEYS |
see defaults.py |
Payload keys whose values are blanked before a row is written |
EVENT_LOG_BUFFER_SIZE |
1000 |
Events held in memory while the writer is behind; a full buffer drops the event rather than making a send wait |
EVENT_LOG_BATCH_SIZE |
200 |
Rows per bulk_create |
EVENT_LOG_FLUSH_INTERVAL |
1 |
Seconds before a partial batch is written anyway |
EVENT_LOG_RETENTION_DAYS |
0 |
Days a row is kept; 0 keeps them for ever. Read by manage.py tgbot_prune_events, never on the write path |
EVENT_LOG_DATABASE |
'' |
A DATABASES alias for the log; empty means the default one |
EVENT_LOG_SYNC |
False |
Write on the calling thread instead of the writer's. Tests only |
EVENT_LOG_KINDS and EVENT_LOG_REDACT_KEYS are settings-only, like
WEBHOOK_ALLOWED_UPDATES: a tuple has no textual form the environment could
carry, and a string from it would be read one character per item.
Check ids¶
Errors are django_aiogram.EXXX, warnings django_aiogram.WXXX,
and information django_aiogram.IXXX. An error refuses the boot; a warning
fails manage.py check --fail-level WARNING, which is what a CI step or an
entrypoint usually runs; information fails neither, and is there for conditions
this package can see but cannot judge from inside a check — I001 and I002
below are both of that kind, because a system check cannot tell which process it
is running in or look inside a database router.
They moved from telegram_bot.EXXX in 2.0 — update SILENCED_SYSTEM_CHECKS
if you silenced any. An id is never reused once its setting is gone, so an
entry naming a retired one is dead but harmless.
| Id | Meaning |
|---|---|
W001 / W002 |
TOKEN / REDIS_URL empty while the bot is enabled |
W003 |
TELEGRAM_BOT contains unknown keys |
W004 |
BLPOP_TIMEOUT is above the ceiling the consumer applies — min(HEARTBEAT_INTERVAL, floor(<the transport timeout>) - 1), never below 1 — so the take is silently shortened to it. Equal to the ceiling is not warned about and is not shortened. The hint names whichever of the two binds, and the transport term is the one BROKER names rather than always REDIS_TIMEOUT |
E001–E003, E017, E049 |
a boolean setting holds something that cannot be read as true or false. ENABLED and AUTODISCOVER are read while the app loads, so in practice those two refuse the boot with the same message before check runs at all |
E004–E007, E009–E011 |
a string setting is wrong, or not one of the allowed values |
E012, E014 |
an integer setting is wrong or below its minimum |
E015 / E016 |
DEFAULT_KWARGS not callable / DEFAULT_BOT_PROPERTIES not a mapping |
E018 |
unknown key in DEFAULT_BOT_PROPERTIES |
E019 |
FSM_STORAGE is not redis, memory or a dotted path — or it names redis on an install carrying no redis-py, which is the default on a project that installed another transport's extra alone |
E020 |
RATE_LIMIT is malformed: an unknown budget name, or one holding something that is not a finite non-negative number. nan is refused although it is a float — every comparison against it is false, so it would pass a bound here and then admit every message in the limiter |
E021 |
WORKER_NAME is not a string |
E022 |
SERIALIZER is pickle while ALLOW_PICKLE is False |
E023 |
HEARTBEAT_INTERVAL is wrong or below 1 |
E024 |
HEALTHCHECK_MAX_QUEUE is wrong or negative |
E025 / E026 |
WEBHOOK_URL / WEBHOOK_SECRET is not a string |
E027 |
WEBHOOK_URL is set without a secret or is not https, or MODE is webhook with no URL |
E028 |
MODE is not polling or webhook |
E029 |
WEBHOOK_ALLOWED_UPDATES is not a collection of update types — a list, a tuple or a set, and any other collection of strings that is not a mapping — or names one Telegram does not have. A mapping is refused although it is a collection — empty ones included — because it is a collection of its keys, so it would register those and drop the values silently |
E030 |
REDIS_TIMEOUT is wrong or below 2 — the pop has to sit one second inside it |
E031, E042 |
EVENT_LOG / EVENT_LOG_SYNC cannot be read as true or false. EVENT_LOG is read while the app loads, so it too refuses the boot first |
E032, E035 |
EVENT_LOG_KINDS / EVENT_LOG_REDACT_KEYS is not a collection of strings — a list, a tuple or a set, and any other that is not a mapping. A mapping is refused for the reason E029 gives |
E033 |
EVENT_LOG_PAYLOAD is not none, summary or full |
E034, E039 |
EVENT_LOG_MAX_PAYLOAD_BYTES / EVENT_LOG_RETENTION_DAYS is wrong or negative |
E036–E038 |
EVENT_LOG_BUFFER_SIZE / EVENT_LOG_BATCH_SIZE / EVENT_LOG_FLUSH_INTERVAL is wrong or below 1 |
E040 |
EVENT_LOG_DATABASE is not a string |
E041 |
EVENT_LOG_DATABASE names an alias that is not in DATABASES |
E043 |
REDIS_URL sets decode_responses while ALLOW_PICKLE is True |
E044 |
DRAIN_TIMEOUT is not a finite number, or is negative |
E045 |
MAX_IN_FLIGHT is not an integer, or is negative |
E046 |
REQUIRE_CRASH_SAFE cannot be read as true or false |
E047 |
BROKER is unusable. Reported whatever ENABLED says: it is empty; it names something that is not a broker; it names one that declares no CALL_TIMEOUT_OPTION — the option bounding one of its calls, which W004 quotes and the consumer caps its reads by; or that option holds something the transport refuses, meaning anything but a positive finite number of seconds, and whatever narrower range the transport documents. That last finding stands aside where the option has a rule of its own that is already reporting the value — REDIS_TIMEOUT has E030, so one value never draws two errors. The name and the deadline are judged before the driver is looked for, so a process that has not installed the extra yet still hears about them. Gated on the bot being enabled, like W001 and W002: the driver behind it is not installed — the hint carries the pip install line for that extra — and its own required settings are unset. A process that never reaches a transport is not asked to install a driver, while a name or a deadline is as wrong in the web tier as in the worker |
E048 |
DATABASE_ROUTERS names any django_redis_aiogram. path, which 4.0 renamed. The router we shipped is named against its replacement, django_aiogram.eventlog.dbrouter.TelegramEventLogRouter; any other path from that distribution is reported as gone, since this cannot invent a replacement for something it never had |
I001 |
WORKER_NAME is empty and the hostname is one Docker generated, so a replacement container gets a different name — which strands whatever the old container was sending. Information rather than a warning because a check cannot tell a consumer from a web process, and every container without hostname: matches; start_tgbot warns for itself at startup |
I003 |
django_redis_aiogram_event — the table 3.x wrote to — is still on whichever database the log resolves to, holding rows this release does not read. Information because leaving them there is a legitimate choice and a check cannot tell it from an oversight; always on, because it is what makes manage.py tgbot_move_events discoverable. Asked of the log's alias rather than the default, so a project with EVENT_LOG_DATABASE set is not the one that hears nothing |
I002 |
EVENT_LOG_DATABASE names an alias and nothing in DATABASE_ROUTERS that this check can read sends this app there — a dotted path counts, and so does an instance, but a bare class does not: Django uses a non-string entry as it stands, so its db_for_read would be called without one. So a plain migrate may not create the table — migrate --database=<alias> still would. Information rather than a warning: a router of your own returning that alias is equally correct, and this cannot see inside one |
W005 |
the log is on while its database has no engine, so every event is dropped |
W006 |
the log is on with EVENT_LOG_RETENTION_DAYS at 0, so nothing ever deletes a row |
W007 |
EVENT_LOG_BATCH_SIZE is above EVENT_LOG_BUFFER_SIZE, so the batch can never fill |
W008 |
EVENT_LOG_KINDS names a kind nothing records |
W009 |
EVENT_LOG_SYNC is on, so a send waits for the database |