diff --git a/press/agent.py b/press/agent.py index 439c9dddca1..985b2d1ec00 100644 --- a/press/agent.py +++ b/press/agent.py @@ -1208,8 +1208,11 @@ def get_site_sid(self, site, user=None): result = self.get(f"benches/{site.bench}/sites/{site.name}/sid") return result and result.get("sid") - def get_site_info(self, site): - result = self.get(f"benches/{site.bench}/sites/{site.name}/info") + def get_site_info(self, site, database_only=False): + path = f"benches/{site.bench}/sites/{site.name}/info" + if database_only: + path += "?database_only=1" + result = self.get(path) if result: return result["data"] return None diff --git a/press/press/doctype/site/site.py b/press/press/doctype/site/site.py index 3565a6575c7..02ff26edf8d 100644 --- a/press/press/doctype/site/site.py +++ b/press/press/doctype/site/site.py @@ -2065,9 +2065,9 @@ def get_login_sid(self, user: str = "Administrator"): frappe.throw(f"Could not login as {user}", frappe.ValidationError) # nosemgrep return sid - def fetch_info(self): + def fetch_info(self, database_only=False): agent = Agent(self.server) - return agent.get_site_info(self) + return agent.get_site_info(self, database_only=database_only) def fetch_analytics(self): agent = Agent(self.server) @@ -2107,6 +2107,24 @@ def _sync_config_info(self, fetched_config: dict) -> bool: return True return False + def _sync_database_usage(self, fetched_usage: dict): + """Record a Site Usage row, refreshing only the database size. + + Carries the last-known file sizes forward so a database-usage refresh + doesn't trigger the expensive file-tree walk in the agent's get_usage. + """ + last = self.get_disk_usages() + self._insert_site_usage( + { + "database": fetched_usage["database"], + "database_free": fetched_usage.get("database_free", 0), + "database_free_tables": fetched_usage.get("database_free_tables", []), + "public": last["public"] or 0, + "private": last["private"] or 0, + "backups": last["backups"] or 0, + } + ) + def _sync_usage_info(self, fetched_usage: dict): """Generate a Site Usage doc for the site using the fetched_usage data. @@ -2184,14 +2202,18 @@ def _sync_database_name(self, config): return False @frappe.whitelist() - def sync_info(self, data=None): + def sync_info(self, data=None, database_only: bool = False): """Updates Site Usage, site.config and timezone details for site.""" if not data: - data = self.fetch_info() + data = self.fetch_info(database_only=database_only) if not data: return + if database_only: + self._sync_database_usage(data["usage"]) + return + fetched_usage = data["usage"] fetched_config = data["config"] fetched_timezone = data["timezone"] @@ -3942,7 +3964,7 @@ def add_database_index(self, table, column): def refresh_database_usage(self): # Check if schema parser enabled on db server if not frappe.db.get_value("Database Server", self.database_server_name, "enable_schema_size_parser"): - self.sync_info() + self.sync_info(database_only=True) return { "synced": True, } @@ -5498,7 +5520,7 @@ def process_refresh_database_usage_job_update(job: AgentJob): site: Site = frappe.get_doc("Site", job.site) with suppress(Exception): # Don't throw error on failure of syncing also - site.sync_info() + site.sync_info(database_only=True) def on_doctype_update(): diff --git a/press/press/doctype/site/test_site.py b/press/press/doctype/site/test_site.py index 5707e427a8e..2af66423b45 100644 --- a/press/press/doctype/site/test_site.py +++ b/press/press/doctype/site/test_site.py @@ -580,6 +580,31 @@ def test_site_usage_exceed_tracking(self): self.assertIsNotNone(site.site_usage_exceeded_on) self.assertEqual(site.status, "Active") + def test_sync_info_database_only_refreshes_db_size_and_carries_files_forward(self): + site = create_test_site() + frappe.get_doc( + { + "doctype": "Site Usage", + "site": site.name, + "database": 100, + "public": 200, + "private": 300, + "backups": 400, + } + ).insert() + + # In database_only mode the agent returns only the database size; the + # file totals must be carried forward, not recomputed (no file walk). + with patch.object(Site, "fetch_info", return_value={"usage": {"database": 150}}) as fetch_info: + site.sync_info(database_only=True) + + fetch_info.assert_called_once_with(database_only=True) + latest = frappe.get_last_doc("Site Usage", {"site": site.name}) + self.assertEqual(latest.database, 150) + self.assertEqual(latest.public, 200) + self.assertEqual(latest.private, 300) + self.assertEqual(latest.backups, 400) + def test_free_sites_ignore_usage_exceed_tracking(self): team = create_test_team(free_account=False) plan_10 = create_test_plan("Site", price_usd=10.0, price_inr=750.0, plan_name="USD 10")