<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Mohammad Ismail Mirza</title>
    <description>The latest articles on Forem by Mohammad Ismail Mirza (@ismailmirza).</description>
    <link>https://forem.com/ismailmirza</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1193856%2F9919f7d5-169d-46ce-b435-23d40ec136c3.jpeg</url>
      <title>Forem: Mohammad Ismail Mirza</title>
      <link>https://forem.com/ismailmirza</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ismailmirza"/>
    <language>en</language>
    <item>
      <title>The Silent Killer: How One Django Signal Can Crashed your AI Support Platform ?</title>
      <dc:creator>Mohammad Ismail Mirza</dc:creator>
      <pubDate>Mon, 01 Dec 2025 11:42:12 +0000</pubDate>
      <link>https://forem.com/ismailmirza/the-silent-killer-how-one-django-signal-can-crashed-your-ai-support-platform--24jc</link>
      <guid>https://forem.com/ismailmirza/the-silent-killer-how-one-django-signal-can-crashed-your-ai-support-platform--24jc</guid>
      <description>&lt;p&gt;You've been coding for hours.&lt;br&gt;
The feature is elegant.&lt;br&gt;
Tests pass.&lt;br&gt;
Code review looks great.&lt;br&gt;
You hit &lt;strong&gt;merge&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Forty-five minutes later, your Slack lights up. Not a polite ping—an explosion.&lt;/p&gt;

&lt;p&gt;🚨 Red alerts.&lt;br&gt;
😤 Customer complaints.&lt;br&gt;
😰 Your CEO asking, "What's happening?"&lt;/p&gt;

&lt;p&gt;Your AI support agents—the ones customers &lt;em&gt;love&lt;/em&gt;—are frozen.&lt;/p&gt;

&lt;p&gt;Conversations stuck.&lt;br&gt;
ChromaDB timing out.&lt;br&gt;
Database maxed out.&lt;/p&gt;

&lt;p&gt;Everything you built is collapsing in real time.&lt;/p&gt;

&lt;p&gt;By the end, you'll trace it to &lt;em&gt;three innocent lines of code you wrote on a quiet Tuesday morning&lt;/em&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  TL;DR — The One-Line Disaster
&lt;/h2&gt;

&lt;p&gt;Calling &lt;code&gt;.save()&lt;/code&gt; inside a Django signal caused:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recursive writes&lt;/li&gt;
&lt;li&gt;Triggered related signals
&lt;/li&gt;
&lt;li&gt;Overwhelmed the database&lt;/li&gt;
&lt;li&gt;Blocked Celery workers&lt;/li&gt;
&lt;li&gt;Full system meltdown under load&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Signals should &lt;em&gt;never&lt;/em&gt; modify models. Use them only to queue async work after commit.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  The Setup: Everything &lt;em&gt;Looked&lt;/em&gt; Perfect
&lt;/h2&gt;

&lt;p&gt;We built a clean pipeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users send support questions&lt;/li&gt;
&lt;li&gt;LangGraph agents (OpenAI-powered) respond&lt;/li&gt;
&lt;li&gt;Messages get stored&lt;/li&gt;
&lt;li&gt;Embeddings added to ChromaDB for semantic search&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our models were straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SupportConversation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;agent_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;active&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DateTimeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auto_now&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConversationMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;conversation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SupportConversation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 'user' or 'assistant'
&lt;/span&gt;    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;embedding_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then one Tuesday morning, a developer added this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@receiver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_save&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ConversationMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_message_saved&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;assistant&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;generate_embeddings_task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# ← The ticking time bomb
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It seemed harmless.&lt;br&gt;
Tests passed.&lt;br&gt;
Staging worked.&lt;br&gt;
10 users? No problem.&lt;/p&gt;

&lt;p&gt;And then &lt;strong&gt;Friday happened&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Meltdown: How It All Fell Apart
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;11:35 AM&lt;/strong&gt; — Marketing launches a flash promo.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Try our AI support agents—free for 24 hours."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Within 60 seconds, &lt;strong&gt;500 fresh chat sessions&lt;/strong&gt; flood in.&lt;/p&gt;
&lt;h3&gt;
  
  
  What happens next:
&lt;/h3&gt;

&lt;p&gt;A user sends a message →&lt;br&gt;
AI responds →&lt;br&gt;
&lt;code&gt;ConversationMessage&lt;/code&gt; saves →&lt;br&gt;
Signal fires →&lt;br&gt;
Celery task queued →&lt;br&gt;
&lt;code&gt;conversation.save()&lt;/code&gt; triggers →&lt;br&gt;
Another signal fires →&lt;br&gt;
Database write →&lt;br&gt;
Repeat… and repeat… and repeat.&lt;/p&gt;

&lt;p&gt;Each conversation generates &lt;em&gt;multiple&lt;/em&gt; unexpected writes.&lt;/p&gt;
&lt;h3&gt;
  
  
  The chain reaction unfolds:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;11:36:00 — DB connection pool: 48/50 in use
11:36:10 — Celery workers blocking, waiting for DB
11:36:20 — ChromaDB calls backing up behind DB queue
11:36:30 — 5,000 tasks in queue
11:36:50 — 25,000 tasks queued
11:37:00 — Everything is stuck
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Messages stop saving.&lt;br&gt;
Requests time out.&lt;br&gt;
ChromaDB errors.&lt;/p&gt;
&lt;h3&gt;
  
  
  And then Slack explodes:
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;@oncall:&lt;/strong&gt; "AI support is frozen."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;@product:&lt;/strong&gt; "Customers can't get replies."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/infra"&gt;@infra&lt;/a&gt;:&lt;/strong&gt; "Why are Celery workers dead?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/devops"&gt;@devops&lt;/a&gt;:&lt;/strong&gt; "DB CPU at 100%, restarting now..."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The platform collapses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recovery time: 4+ hours.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The root cause? &lt;strong&gt;Nested signals triggering recursive &lt;code&gt;.save()&lt;/code&gt; calls.&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Root Cause: The Recursive Death Spiral
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ConversationMessage.save()
    ↓
post_save signal fires
    ↓
instance.conversation.save()  ← nested save
    ↓
post_save (SupportConversation) fires
    ↓
Model writes again
    ↓
Triggers more signals
    ↓
More writes under load = exponential explosion
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Under high concurrency, this becomes an &lt;em&gt;accidental DDoS against your own database&lt;/em&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Four Fixes That Saved Us
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Always Guard with &lt;code&gt;created&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Ensure the handler fires &lt;strong&gt;only once&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.db.models.signals&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;post_save&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.dispatch&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;receiver&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;

&lt;span class="nd"&gt;@receiver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_save&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ConversationMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;queue_message_processing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# ✅ Only run on NEW messages
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;assistant&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# ✅ Queue AFTER transaction commits (no nested saves)
&lt;/span&gt;        &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on_commit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;generate_embeddings_task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No repeated firing&lt;/li&gt;
&lt;li&gt;No nested writes
&lt;/li&gt;
&lt;li&gt;The async task runs only after the DB transaction commits&lt;/li&gt;
&lt;li&gt;Clean separation of concerns&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Let Celery Update Models Safely
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;update_fields&lt;/code&gt; to skip signal handlers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;celery&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;shared_task&lt;/span&gt;

&lt;span class="nd"&gt;@shared_task&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_embeddings_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConversationMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;message_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Call OpenAI API
&lt;/span&gt;    &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text-embedding-3-small&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;

    &lt;span class="c1"&gt;# Store in ChromaDB
&lt;/span&gt;    &lt;span class="n"&gt;chroma_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;msg_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# ✅ CRITICAL: Use update_fields to skip the signal handler
&lt;/span&gt;    &lt;span class="n"&gt;ConversationMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;message_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;embedding_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;msg_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Update your handler to recognize this:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@receiver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_save&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ConversationMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;queue_message_processing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;update_fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# ✅ If only embedding_id changed, skip entirely
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;update_fields&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;update_fields&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;embedding_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="c1"&gt;# ... rest of handler
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Celery can safely modify models&lt;/li&gt;
&lt;li&gt;Queryset updates with &lt;code&gt;update_fields&lt;/code&gt; bypass post_save signals&lt;/li&gt;
&lt;li&gt;No recursive loops even under heavy load&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Use QuerySet Updates for Parent Stats
&lt;/h3&gt;

&lt;p&gt;❌ &lt;strong&gt;DON'T do this:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# ❌ triggers another signal!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;DO this instead:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;SupportConversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;message_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversationmessage_set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;QuerySet operations bypass signals entirely&lt;/li&gt;
&lt;li&gt;No cascade&lt;/li&gt;
&lt;li&gt;Atomic database operation&lt;/li&gt;
&lt;li&gt;10x faster&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. Batch Updates Instead of Per-Message
&lt;/h3&gt;

&lt;p&gt;Instead of firing stats updates on every message, batch them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;celery&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;shared_task&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.db.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Count&lt;/span&gt;

&lt;span class="nd"&gt;@shared_task&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;batch_update_conversation_stats&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Run every 30 seconds via Celery Beat.
    Update all active conversations at once.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;conversations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SupportConversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;active&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;conversationmessage&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;conv&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;conversations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;SupportConversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;conv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;message_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;conv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;msg_count&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# In celery.py, schedule with:
# 'batch-update-stats': {
#     'task': 'myapp.tasks.batch_update_conversation_stats',
#     'schedule': crontab(minute='*/1'),  # Every minute
# }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The math:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;500 concurrent messages = 500 individual updates (❌ cascades)&lt;/li&gt;
&lt;li&gt;500 concurrent messages = 1 batch update every 30 seconds (✅ efficient)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Testing for Signal Cascades (Critical!)
&lt;/h2&gt;

&lt;p&gt;You &lt;strong&gt;must&lt;/strong&gt; test this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestCase&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.test.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CaptureQueriesContext&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConversationSignalTests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_message_creation_is_silent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        Verify: Creating ONE message = ONE database write.
        If this fails, you have a signal loop.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;conv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SupportConversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test_user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;CaptureQueriesContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;ConversationMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;conv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Help!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;tokens_used&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;response_time_ms&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;writes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;captured_queries&lt;/span&gt; 
                  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sql&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;INSERT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;UPDATE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;writes&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Expected 1 write, got &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;writes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Signal loop detected!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_load_no_cascade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        Simulate production load: 100 concurrent messages.
        Verify no cascade explosion.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;convs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;SupportConversation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;CaptureQueriesContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;conv&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;convs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;ConversationMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;conversation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;conv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Question&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;tokens_used&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;response_time_ms&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;writes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;captured_queries&lt;/span&gt; 
                  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sql&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;INSERT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;UPDATE&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertLess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;writes&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Too many writes: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;writes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Cascade detected!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Best Practices Checklist
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✔ Always Do:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ Use the &lt;code&gt;created&lt;/code&gt; flag guard&lt;/li&gt;
&lt;li&gt;✅ Use &lt;code&gt;transaction.on_commit()&lt;/code&gt; for async work&lt;/li&gt;
&lt;li&gt;✅ Keep signals &lt;strong&gt;pure&lt;/strong&gt;—never modify models&lt;/li&gt;
&lt;li&gt;✅ Use &lt;code&gt;queryset.update()&lt;/code&gt;, never &lt;code&gt;.save()&lt;/code&gt; in handlers&lt;/li&gt;
&lt;li&gt;✅ Use &lt;code&gt;update_fields&lt;/code&gt; when Celery updates models&lt;/li&gt;
&lt;li&gt;✅ Batch expensive tasks (stats, aggregations)&lt;/li&gt;
&lt;li&gt;✅ Stress test with concurrent load&lt;/li&gt;
&lt;li&gt;✅ Monitor signal depth in production&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ✘ Never Do:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;❌ Call &lt;code&gt;.save()&lt;/code&gt; inside a signal handler&lt;/li&gt;
&lt;li&gt;❌ Trigger parent model saves from child signals&lt;/li&gt;
&lt;li&gt;❌ Assume "it works locally" means "it scales"&lt;/li&gt;
&lt;li&gt;❌ Trust signals under load without testing&lt;/li&gt;
&lt;li&gt;❌ Create circular signal dependencies&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Lesson: The Signal Rule
&lt;/h2&gt;

&lt;p&gt;Django signals are powerful.&lt;br&gt;
They're also &lt;strong&gt;silent footguns&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One innocent &lt;code&gt;.save()&lt;/code&gt; buried in a signal handler can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💥 Hammer your database&lt;/li&gt;
&lt;li&gt;🧊 Freeze your async workers&lt;/li&gt;
&lt;li&gt;📉 Cascade into platform-wide downtime&lt;/li&gt;
&lt;li&gt;😞 Ruin your Friday&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The rule is simple:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Signals should queue async work—not change database state.&lt;br&gt;
Celery should handle all updates.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Follow this, and you'll never meet the monster we met at 11:36 AM on a Friday.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Signal fires
  ↓
Queue Celery task
  ↓
Commit transaction
  ↓
Celery task runs safely
  ↓
Updates database (signal-free)
  ↓
✅ No cascade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.djangoproject.com/en/stable/topics/signals/" rel="noopener noreferrer"&gt;Django Signals Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.djangoproject.com/en/stable/topics/db/transactions/#django.db.transaction.on_commit" rel="noopener noreferrer"&gt;Django Transactions &amp;amp; on_commit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.celeryproject.io/en/stable/userguide/tasks.html" rel="noopener noreferrer"&gt;Celery Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.trychroma.com/" rel="noopener noreferrer"&gt;ChromaDB Python Client&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Last updated: December 1, 2025&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Have your own signal horror story? Drop it in the comments—you're not alone.&lt;/em&gt; 🚀&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>api</category>
      <category>backend</category>
    </item>
    <item>
      <title>Top 10 ReactJs Books to Enhance Your Frontend Skills [2024]</title>
      <dc:creator>Mohammad Ismail Mirza</dc:creator>
      <pubDate>Thu, 23 May 2024 19:10:36 +0000</pubDate>
      <link>https://forem.com/ismailmirza/top-10-reactjs-books-to-enhance-your-frontend-skills-1700</link>
      <guid>https://forem.com/ismailmirza/top-10-reactjs-books-to-enhance-your-frontend-skills-1700</guid>
      <description>&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;React JS is now one of the most liked JavaScript tools for making user parts on the web. If you want to get better at React, getting the best help is key. There are lots of books that can give deep know-how and help you get better at using React to its full. In this piece, we made a list of the top 10 books that will lift your web skills. No matter if you are new or good at this, these books can help you to get strong at React and stay up in the tech world that keeps on going.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Understanding the importance of continuous learning in web development
&lt;/h2&gt;

&lt;p&gt;In the fast-changing world of tech, making your web-making skills better is very needed. As you get better at using React, it's key to know that non-stop learning is the main thing to get new ideas and keep up with others. Use web help, join short learning events, and talk with other code creators to keep up with the newest styles. In the next part, we'll look at why knowing the newest moves in the field is key and how this can make you much better at making things with React. Keep going through this to find out how learning all the time can lift your job in making websites to new high points.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Exploring the top React books for mastering the framework
&lt;/h2&gt;

&lt;p&gt;Learn more about React and take a quick look at the top books to read to expand your knowledge. Here is a list of resources to continue learning, giving more detailed information, cases, and recommendations on how to make the most of using React. From fundamental to the most advanced web development issues, you will find all the boost you need to tackle some of the most advanced web development issues in these books. Are you looking to expand your understanding and get more proficient with React? Following is a list of ten most recommended books that will guide you towards gaining new heights in web development. As for recommendation, stick around and be ready to improve your knowledge on React!&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1. Learning React: Modern Patterns for Developing React Apps by Alex Banks and Eve Porcello
&lt;/h3&gt;

&lt;p&gt;If you want to learn how to build efficient React applications, this is your book. Ideal for web developers and software engineers who understand how JavaScript, CSS, and HTML work in the browser, this updated edition provides best practices and patterns for writing modern React code. No prior knowledge of React or functional JavaScript is necessary. With their learning road map, authors Alex Banks and Eve Porcello show you how to create UIs that can deftly display changes without page reloads on large-scale, data-driven websites. You'll also discover how to work with functional programming and the latest ECMAScript features. Once you learn how to build React components with this hands-on guide, you'll understand just how useful React can be in your organization. Download this book for free &lt;a href="https://pdfbd.top/books/learning-react-modern-patterns-for-developing-react-apps-by-alex-pdf"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz3sip3k6gm590ukj4qar.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz3sip3k6gm590ukj4qar.jpg" alt="learning react modern patterns book cover" width="126" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2. Learn React with TypeScript: 2nd Edition by Carl Rippon (2023)
&lt;/h3&gt;

&lt;p&gt;If you are a developer who is already familiar with either TypeScript or React and wants to understand how they can be used together effectively then this book is for you. Getting a handle on the challenges of modern React development is not easy. In this course, Carl Rippon helps by going beyond your first React app and by examining such issues as how to design useful components (and avoid overloading them), how to state manage with TypeScript without losing clear coupling between bits of data, hooks versus classes and what goes where in a real-world solution stack. Download this book for free or read online &lt;a href="https://pdfbd.top/books/learn-react-with-typescript-2nd-edition-by-carl-rippon-2023-pdf"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijmi90h53el5k7wuinkm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijmi90h53el5k7wuinkm.png" alt="learn react with typescript book cover" width="701" height="873"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3. Mastering React by Adam Horton and Ryan Vice (2016)
&lt;/h3&gt;

&lt;p&gt;Master the art of building modern web applications using React. Write a complete application in React using an array of supporting libraries, both specifically React-related and general purpose. Understand what makes React stand apart from the vast majority of JS frameworks available through detailed explanations and concise examples. Explore the React ecosystem and how to integrate React with other modern web technologies. This book is ideal for web developers possessing strong core JavaScript fundamentals who are also interested in learning what React brings to the architectural table. Download this book for free &lt;a href="https://pdfbd.top/books/mastering-react-by-adam-horton-ryan-vice-2016-pdf"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4. Mastering Reactive JavaScript by Erich de Souza Oliveira (2017)
&lt;/h3&gt;

&lt;p&gt;Expand your boundaries by creating applications empowered with real-time data using ReactJS without compromising performance. Handle an infinite stream of incoming data using ReactJS without going crazy. Explore important ReactJS operators that can help you improve your code readability. Get acquainted with the different techniques and operators used to handle data traffic, which occurs when you receive data faster than you can process. If you're a web developer with some basic JavaScript programming knowledge who wants to implement the reactive programming paradigm with JavaScript, then this book is for you. Download this book for free &lt;a href="https://pdfbd.top/books/mastering-reactive-javascript-erich-de-souza-oliveira-2017-pdf"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.5. React Cookbook By David Griffiths
&lt;/h3&gt;

&lt;p&gt;React helps you create and work on an app in just a few minutes. But learning how to put all the pieces together is hard. How do you validate a form? Or implement a complex multistep user action without writing messy code? How do you test your code? Make it reusable? Wire it to a backend? Keep it easy to understand? The React Cookbook delivers answers fast. Many books teach you how to get started, understand the framework, or use a component library with React, but very few provide examples to help you solve particular problems. This easy-to-use cookbook includes the example code developers need to unravel the most common problems when using React, categorized by topic area and problem. Download this book for free &lt;a href="https://pdfbd.top/books/mastering-react-beginners-guide-pdf"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.6. Mastering React Native: Beginners Guide
&lt;/h3&gt;

&lt;p&gt;Mastering React Native helps the reader master the React Native framework for faster and more robust mobile app development. React Native is an open-source JavaScript framework that allows you to create applications for many platforms, including iOS, Android, and the web, all with the same code base. It is built on the React framework, and it provides all of React's power to mobile app development. React Native was a natural continuation of React. It is a mobile framework that includes JavaScript to create near-native apps. JSX, a hybrid of JavaScript and XML-like markup, is used to make React Native applications. The React Native "bridge" then calls the native rendering APIs in Objective-C (for iOS) or Java (for Android) behind the scenes. Download this book for free &lt;a href="https://pdfbd.top/books/mastering-react-native-beginners-pdf"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.7. Mastering React Test-Driven Development: 2nd Edition
&lt;/h3&gt;

&lt;p&gt;Learn test-driven and behavior-driven development techniques that will give you greater confidence when building React applications. Explore the TDD process, how it works, and why it will help you write maintainable React apps. Develop a component testing framework from scratch, which will help you understand the mechanics of good unit testing. Reduce complexity by using unit tests and end-to-end acceptance tests to drive the design of your apps. Test-driven development (TDD) is a programming workflow that helps you build your apps by specifying behavior as automated tests. The TDD workflow future-proofs apps so that they can be modified without fear of breaking existing functionality. Download this book for free &lt;a href="https://pdfbd.top/books/mastering-react-test-driven-development-2nd-pdf"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.8. React Key Concepts by Maximilian Schwarzmüller (2022)
&lt;/h3&gt;

&lt;p&gt;Build the understanding, skills, and experience to confidently implement React in your next project with this fast-paced overview of React fundamentals. A clear, concise explanation of core React 18 functionalities to promote quick, easy reference. Gain a deep understanding of key React concepts with the help of step-by-step derivations. Work with practical exercises that challenge you to apply your new skills and build your own simple apps. As the most popular JavaScript library for building modern, interactive user interfaces, React is an in-demand framework that'll bring real value to your career or next project. Download this book for free &lt;a href="https://pdfbd.top/books/maximilian-schwarzmuller-react-key-concepts-2022-pdf"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.9. Micro State Management with React Hooks
&lt;/h3&gt;

&lt;p&gt;Understand the essential concepts and features of micro state management. Discover solutions to common problems faced while implementing micro state management. Explore the different libraries, their coding style, and the optimum approach to rendering optimization. State management is one of the most complex concepts in React. Traditionally, developers have used monolithic state management solutions. Thanks to React Hooks, micro state management is something tuned for moving your application from a monolith to a microservice. Download this book for free &lt;a href="https://pdfbd.top/books/micro-state-management-react-hooks-pdf"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.10. Modern Full-Stack Development: Second Edition
&lt;/h3&gt;

&lt;p&gt;Discover how to build and deploy full-stack applications with modern technologies like React, Node.js, and GraphQL. This comprehensive guide covers everything from setting up your development environment to deploying your application. Learn how to build scalable and maintainable applications by following best practices and using the latest tools and frameworks. Download this book for free &lt;a href="https://pdfbd.top/books/modern-full-stack-development-2nd-pdf"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Conclusion
&lt;/h2&gt;

&lt;p&gt;If you want to download more books on React, you can visit this site &lt;a href="https://www.pdfbard.com"&gt;pdfbard&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Thanks for reading this, stay tuned for more.&lt;/p&gt;

&lt;p&gt;Thanks for reading; if you liked my content and want to support me, the best way is to -&lt;/p&gt;

&lt;p&gt;&lt;a href="https://t.me/pdfbard"&gt;Join our telegram channel to get the latest books as soon as possible.&lt;/a&gt;&lt;br&gt;
&lt;a href="https://facebook.com/pdfbard"&gt;Connect with me on Facebook, where I keep sharing such free content to become more productive at your life&lt;/a&gt;.&lt;br&gt;
Follow me on X (&lt;a href="https://x.com/pdfbard"&gt;Twitter&lt;/a&gt;) and &lt;a href="https://pdfbard.medium.com"&gt;Medium&lt;/a&gt; to get instant notifications for everything new.&lt;br&gt;
Join my Pinterest for upcoming insightful content.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>reactjsdevelopment</category>
      <category>javascript</category>
      <category>programming</category>
      <category>reactnative</category>
    </item>
    <item>
      <title>Authenticate nextjs application with hanko frontend auth.</title>
      <dc:creator>Mohammad Ismail Mirza</dc:creator>
      <pubDate>Sun, 07 Jan 2024 14:54:59 +0000</pubDate>
      <link>https://forem.com/ismailmirza/authenticate-nextjs-application-with-hanko-frontend-auth-4i39</link>
      <guid>https://forem.com/ismailmirza/authenticate-nextjs-application-with-hanko-frontend-auth-4i39</guid>
      <description>&lt;p&gt;Hanko is a lightweight, open source user authentication solution that takes you on the journey beyond passwords.&lt;br&gt;
&lt;strong&gt;Integrate Hanko with Next.js&lt;/strong&gt;&lt;br&gt;
Learn how to quickly add authentication and user profile in your Next.js app using Hanko.&lt;br&gt;
First Create New Nextjs app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-next-app@latest nextjs-blog --use-npm --example "https://github.com/vercel/next-learn/tree/main/basics/learn-starter BLOGapp"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now install hanko frontend-elements&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add @teamhanko/hanko-elements

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;now run development server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now go to &lt;a href="https://cloud.hanko.io"&gt;https://cloud.hanko.io&lt;/a&gt; and signup for api access.&lt;/p&gt;

&lt;blockquote&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6saqz23t3c1kk8zak8b6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6saqz23t3c1kk8zak8b6.png" alt="Image description" width="800" height="349"&gt;&lt;/a&gt;&lt;br&gt;
enter suitable project name and in app url enter &lt;code&gt;http://localhost:3000&lt;/code&gt; you can access hanko frontend elements from this url only.&lt;br&gt;
Now create &lt;code&gt;.env&lt;/code&gt; file in app directory and copy Hanko api url to .env file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_HANKO_API_URL = &amp;lt;Hanko api url from general setting&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now go to supabase.com create a project and signup and create a project &lt;br&gt;
Now go to project setting and click database on right side &lt;br&gt;
Go to Nodejs and copy the database url and now add this in .env file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_URL = postgresql://postgres:[YOUR-PASSWORD]@db.dshyzhthijimhivhaeyq.supabase.co:5432/postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace password with the password of the project that you entered during the creation of the project...&lt;br&gt;
Now this a good to go.&lt;br&gt;
Install prisma&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add -d prisma
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, set up Prisma with the init command of the Prisma CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn prisma init 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now navigate to prisma folder and  open schema.prisma file &lt;/p&gt;

&lt;p&gt;Add this code top of schema file to connect with frontend provider of db client&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;generator client {
  provider = "prisma-client-js"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now connect with postgresql database&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;datasource db {
  provider     = "postgresql"
  url          = env("DATABASE_URL")
  relationMode = "prisma"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create model named user to store the login user in backend&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

model User {
  id    String @id @unique 
  email String @unique

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run this command to migrate database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn prisma migrate dev --name init

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now create a folder named db in src folder&lt;br&gt;
In index.js copy and paste this code. Here we imported PrismaClient and setup the db for using in backend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { PrismaClient } from '@prisma/client'

declare global {
  // eslint-disable-next-line no-var
  var cachedPrisma: PrismaClient
}

let prisma: PrismaClient
if (process.env.NODE_ENV === 'production') {
  prisma = new PrismaClient()
} else {
  if (!global.cachedPrisma) {
    global.cachedPrisma = new PrismaClient()
  }
  prisma = global.cachedPrisma
}

export const db = prisma
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now app/api/auth/route.ts. In order to serialize cookie we will use&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { db } from "@/db";
import { serialize } from "cookie";




export  async function POST(request:Request) {

    // Handle the POST request here
    // You can access request data using req.body
    const data = await request.json();
    const {id,email} = data;
    if (!id || !email){
        return Response.json({
            message:"Missing Data in body",

        },{
            status:404
        })
    }

    const already_user = await db.user.findFirst({
      where:{
        id:id
      }
    })
    if (already_user)
    { 

      const ck = serialize("user_id",id,{
        secure:process.env.NODE_ENV === "production",
        httpOnly:true,
        path:"/",
      })
      const resp = Response.json({
        message:"User already existed and init login",

    },{
        status:200
    })

    resp.headers.set("Set-Cookie",ck)
    return resp
    }
    try {
        const user = await db.user.create({
          data: {
            id: id,
            email: email,
          },
        });
        const ck = serialize("user_id",user.id,{
          secure:process.env.NODE_ENV === "production",
          httpOnly:true,
          path:"/",
        })
        const resp = Response.json(
          {
              message:"Account is created",
              user: user
          },
          {
              status:201
          }
      )
      resp.headers.set('Set-Cookie',ck);

        return resp
      } catch (error) {
        console.log(error)
        return Response.json({ message: 'Failed to save user' },{status:400})
      }






}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>hanko</category>
    </item>
  </channel>
</rss>
