perf: separate blob storage for registratura attachments
Root cause: even with SQL-level stripping, PostgreSQL must TOAST-decompress entire multi-MB JSONB values from disk before any processing. For 5 entries with PDF attachments (25-50MB total), this takes several seconds. Fix: store base64 attachment data in separate namespace 'registratura-blobs'. Main entries are inherently small (~1-2KB). List queries never touch heavy data. Changes: - registry-service.ts: extractBlobs/mergeBlobs split base64 on save/load, migrateEntryBlobs() one-time migration for existing entries - use-registry.ts: dual namespace (registratura + registratura-blobs), migration runs on first mount - registratura-module.tsx: removed useContacts/useTags hooks that triggered 2 unnecessary API fetches on page load (write-only ops use direct storage) Before: 3 API calls on mount, one reading 25-50MB from PostgreSQL After: 1 API call on mount, reading ~5-10KB total
This commit is contained in:
@@ -24,8 +24,8 @@ import {
|
||||
DialogFooter,
|
||||
} from "@/shared/components/ui/dialog";
|
||||
import { useRegistry } from "../hooks/use-registry";
|
||||
import { useContacts } from "@/modules/address-book/hooks/use-contacts";
|
||||
import { useTags } from "@/core/tagging";
|
||||
import { useStorage, useStorageService } from "@/core/storage";
|
||||
import { v4 as uuid } from "uuid";
|
||||
import { RegistryFilters } from "./registry-filters";
|
||||
import { RegistryTable } from "./registry-table";
|
||||
import { RegistryEntryForm } from "./registry-entry-form";
|
||||
@@ -55,15 +55,16 @@ export function RegistraturaModule() {
|
||||
removeDeadline,
|
||||
} = useRegistry();
|
||||
|
||||
const { addContact } = useContacts();
|
||||
const { createTag } = useTags("document-type");
|
||||
// Direct storage for write-only operations — avoids loading all contacts/tags on mount
|
||||
const contactStorage = useStorage("address-book");
|
||||
const tagStorageService = useStorageService();
|
||||
|
||||
const [viewMode, setViewMode] = useState<ViewMode>("list");
|
||||
const [editingEntry, setEditingEntry] = useState<RegistryEntry | null>(null);
|
||||
const [closingId, setClosingId] = useState<string | null>(null);
|
||||
const [linkCheckId, setLinkCheckId] = useState<string | null>(null);
|
||||
|
||||
// ── Bidirectional Address Book integration ──
|
||||
// ── Bidirectional Address Book integration (write-only, no eager fetch) ──
|
||||
const handleCreateContact = useCallback(
|
||||
async (data: {
|
||||
name: string;
|
||||
@@ -71,7 +72,9 @@ export function RegistraturaModule() {
|
||||
email: string;
|
||||
}): Promise<AddressContact | undefined> => {
|
||||
try {
|
||||
const contact = await addContact({
|
||||
const now = new Date().toISOString();
|
||||
const contact: AddressContact = {
|
||||
id: uuid(),
|
||||
name: data.name,
|
||||
company: "",
|
||||
type: "collaborator",
|
||||
@@ -88,30 +91,36 @@ export function RegistraturaModule() {
|
||||
tags: [],
|
||||
notes: "Creat automat din Registratură",
|
||||
visibility: "all",
|
||||
});
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
await contactStorage.set(`contact:${contact.id}`, contact);
|
||||
return contact;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
[addContact],
|
||||
[contactStorage],
|
||||
);
|
||||
|
||||
// ── Bidirectional Tag Manager integration ──
|
||||
// ── Bidirectional Tag Manager integration (write-only, no eager fetch) ──
|
||||
const handleCreateDocType = useCallback(
|
||||
async (label: string) => {
|
||||
try {
|
||||
await createTag({
|
||||
const tagId = uuid();
|
||||
await tagStorageService.set("tags", tagId, {
|
||||
id: tagId,
|
||||
label,
|
||||
category: "document-type",
|
||||
scope: "global",
|
||||
color: "#64748b",
|
||||
createdAt: new Date().toISOString(),
|
||||
});
|
||||
} catch {
|
||||
// tag may already exist — ignore
|
||||
}
|
||||
},
|
||||
[createTag],
|
||||
[tagStorageService],
|
||||
);
|
||||
|
||||
const handleAdd = async (
|
||||
|
||||
Reference in New Issue
Block a user