From 4dc5c012daeff93c738daa7921cbda1832a63384 Mon Sep 17 00:00:00 2001
From: Jeremie Pardou <jeremie@baserow.io>
Date: Mon, 24 Mar 2025 09:44:55 +0100
Subject: [PATCH] Fix schema cache issue

---
 backend/src/baserow/contrib/database/table/cache.py   |  7 ++++++-
 backend/src/baserow/contrib/database/table/signals.py |  1 +
 .../contrib/integrations/local_baserow/receivers.py   | 11 +++++++++++
 .../integrations/local_baserow/service_types.py       |  2 +-
 backend/src/baserow/contrib/integrations/signals.py   |  5 ++---
 backend/src/baserow/core/cache.py                     |  3 ++-
 ...invalidation_on_field_change_for_data_sources.json |  8 ++++++++
 7 files changed, 31 insertions(+), 6 deletions(-)
 create mode 100644 backend/src/baserow/contrib/integrations/local_baserow/receivers.py
 create mode 100644 changelog/entries/unreleased/bug/fix_cache_invalidation_on_field_change_for_data_sources.json

diff --git a/backend/src/baserow/contrib/database/table/cache.py b/backend/src/baserow/contrib/database/table/cache.py
index 61b0db07b..762761d49 100644
--- a/backend/src/baserow/contrib/database/table/cache.py
+++ b/backend/src/baserow/contrib/database/table/cache.py
@@ -67,11 +67,16 @@ def clear_generated_model_cache():
 
 
 def invalidate_table_in_model_cache(table_id: int):
+    from baserow.contrib.database.table.models import Table
+    from baserow.contrib.database.table.signals import table_schema_changed
+
+    # Send signal for other potential cached values
+    table_schema_changed.send(Table, table_id=table_id)
+
     if settings.BASEROW_DISABLE_MODEL_CACHE:
         return None
 
     new_version = str(uuid.uuid4())
     # Make sure to invalidate ourselves and any directly connected tables.
-    from baserow.contrib.database.table.models import Table
 
     Table.objects_and_trash.filter(id=table_id).update(version=new_version)
diff --git a/backend/src/baserow/contrib/database/table/signals.py b/backend/src/baserow/contrib/database/table/signals.py
index f5dfa690e..7ff819c3a 100644
--- a/backend/src/baserow/contrib/database/table/signals.py
+++ b/backend/src/baserow/contrib/database/table/signals.py
@@ -8,6 +8,7 @@ table_created = Signal()
 table_updated = Signal()
 table_deleted = Signal()
 tables_reordered = Signal()
+table_schema_changed = Signal()
 
 
 @receiver(post_delete, sender=Table)
diff --git a/backend/src/baserow/contrib/integrations/local_baserow/receivers.py b/backend/src/baserow/contrib/integrations/local_baserow/receivers.py
new file mode 100644
index 000000000..5ec278f93
--- /dev/null
+++ b/backend/src/baserow/contrib/integrations/local_baserow/receivers.py
@@ -0,0 +1,11 @@
+from django.dispatch import receiver
+
+from baserow.contrib.database.table.signals import table_schema_changed
+from baserow.core.cache import global_cache, local_cache
+
+
+@receiver(table_schema_changed)
+def invalidate_table_cache(sender, table_id, **kwargs):
+    # Invalidate local cache when the table schema is updated
+    global_cache.invalidate(f"table_{table_id}__service_schema")
+    local_cache.delete(f"integration_service_{table_id}_table_model")
diff --git a/backend/src/baserow/contrib/integrations/local_baserow/service_types.py b/backend/src/baserow/contrib/integrations/local_baserow/service_types.py
index c223e3855..6ad29d144 100644
--- a/backend/src/baserow/contrib/integrations/local_baserow/service_types.py
+++ b/backend/src/baserow/contrib/integrations/local_baserow/service_types.py
@@ -495,7 +495,7 @@ class LocalBaserowTableServiceType(LocalBaserowServiceType):
             return None
 
         properties = global_cache.get(
-            f"table_{service.table_id}_{service.table.version}__service_schema",
+            f"table_{service.table_id}__service_schema",
             default=lambda: self._get_table_properties(service, allowed_fields),
             timeout=SCHEMA_CACHE_TTL,
         )
diff --git a/backend/src/baserow/contrib/integrations/signals.py b/backend/src/baserow/contrib/integrations/signals.py
index da6c8c9ef..0180c90cc 100644
--- a/backend/src/baserow/contrib/integrations/signals.py
+++ b/backend/src/baserow/contrib/integrations/signals.py
@@ -1,7 +1,6 @@
+from baserow.contrib.integrations.local_baserow.receivers import invalidate_table_cache
 from baserow.contrib.integrations.local_baserow.signals import (
     handle_local_baserow_field_updated_changes,
 )
 
-__all__ = [
-    "handle_local_baserow_field_updated_changes",
-]
+__all__ = ["handle_local_baserow_field_updated_changes", "invalidate_table_cache"]
diff --git a/backend/src/baserow/core/cache.py b/backend/src/baserow/core/cache.py
index a679c4625..849c1cd86 100644
--- a/backend/src/baserow/core/cache.py
+++ b/backend/src/baserow/core/cache.py
@@ -65,7 +65,8 @@ class LocalCache:
             ):
                 del self._local.cache[k]
         else:
-            del self._local.cache[key]
+            if key in self._local.cache:
+                del self._local.cache[key]
 
     def clear(self):
         """
diff --git a/changelog/entries/unreleased/bug/fix_cache_invalidation_on_field_change_for_data_sources.json b/changelog/entries/unreleased/bug/fix_cache_invalidation_on_field_change_for_data_sources.json
new file mode 100644
index 000000000..d9fe5d942
--- /dev/null
+++ b/changelog/entries/unreleased/bug/fix_cache_invalidation_on_field_change_for_data_sources.json
@@ -0,0 +1,8 @@
+{
+    "type": "bug",
+    "message": "Fix cache invalidation on field change for data sources",
+    "domain": "builder",
+    "issue_number": null,
+    "bullet_points": [],
+    "created_at": "2025-03-24"
+}
\ No newline at end of file