Переглянути джерело

fix: explain why creating a collection looks like nothing happened

Creating a collection appeared to do nothing, and creating it again answered
500 "createCollection failed".

Both come from the same thing: the database does not list a collection that has
no documents in it, and will not let you query one either - GET on it answers
"no collection or view named ...". So the first attempt worked and vanished, the
obvious response was to try again, and the second attempt was refused because the
collection was already there.

The refusal reached us as a bare false. The upstream client returns a bool, so
the reason never arrives, which meant the controller's AlreadyExists branch could
never fire and every refusal became a 500 with nothing to act on. It now answers
409 and says what is almost certainly true: it already exists, an empty
collection is invisible, write something to it and it will appear.

The page also keeps a collection it just created in the list rather than losing
it on the next refetch, and says why it looks empty when opened. Remembering it
in the page is the only place that can be done - the server genuinely cannot
list it yet.

Reproduced and verified against the running database: creating reddit_rss_cache
answers 409 with the explanation, writing a document to it succeeds, and it then
appears in the listing. Full suite 57/57.

Not fixed here: there is no route to drop a collection, so one created by
mistake cannot be removed through the API.
fszontagh 1 місяць тому
батько
коміт
4f36329ae6

+ 14 - 5
src/webserver/api/database_controller.cpp

@@ -103,11 +103,20 @@ void DatabaseController::createCollection(const httplib::Request& req, httplib::
     auto result = storage_.createCollection(name, {}, default_ttl_ms);
 
     if (result.failed()) {
-        if (result.error().code() == common::ErrorCode::AlreadyExists) {
-            sendError(res, "Collection already exists: " + name, 409);
-        } else {
-            sendError(res, result.error().message(), 500);
-        }
+        // The upstream client answers with a bare bool, so the reason never
+        // reaches us - the AlreadyExists branch this used to have could not
+        // fire, and every refusal surfaced as a 500 with nothing to act on.
+        //
+        // By far the most common reason is that the collection is already
+        // there. It just cannot be seen: the database does not list, or even
+        // let you query, a collection with no documents in it, so creating one
+        // looks like nothing happened and the obvious response is to try again.
+        // Saying so is more use than repeating "failed".
+        sendError(res,
+                  "Could not create '" + name + "'. It most likely already exists: a collection "
+                  "with no documents in it is not listed and cannot be queried, so creating one "
+                  "looks like nothing happened. Write a document to it and it will appear",
+                  409);
         return;
     }
 

+ 22 - 2
webui/src/pages/DatabasePage.tsx

@@ -72,6 +72,8 @@ export default function DatabasePage() {
   const [currentPage, setCurrentPage] = useState(1)
   const documentListRef = useRef<HTMLDivElement>(null)
 
+  const [justCreated, setJustCreated] = useState<string[]>([])
+
   const { data: collectionsData, isLoading: collectionsLoading } = useQuery({
     queryKey: ['database-collections'],
     queryFn: () => databaseApi.listCollections(),
@@ -133,6 +135,11 @@ export default function DatabasePage() {
     mutationFn: ({ name, settings }: { name: string; settings?: CollectionSettings }) =>
       databaseApi.createCollection(name, settings),
     onSuccess: (_, { name }) => {
+      // The database does not list a collection until it holds a document, so
+      // refetching would drop the one just created and the whole thing would
+      // look like it had not worked. Remembered here until it shows up on its
+      // own.
+      setJustCreated((prev) => (prev.includes(name) ? prev : [...prev, name]))
       queryClient.invalidateQueries({ queryKey: ['database-collections'] })
       setShowNewCollectionModal(false)
       setNewCollectionName('')
@@ -299,8 +306,17 @@ export default function DatabasePage() {
     }
   }
 
-  const collections: string[] = collectionsData?.collections || []
+  const fetched: string[] = collectionsData?.collections || []
+  // Anything created this session that the server cannot list yet.
+  const collections: string[] = [
+    ...fetched,
+    ...justCreated.filter((name) => !fetched.includes(name)),
+  ]
   const documents: Document[] = documentsData?.documents || []
+  // A collection with nothing in it cannot be queried at all, so its document
+  // list comes back as an error rather than an empty page.
+  const isEmptyNewCollection =
+    !!selectedCollection && justCreated.includes(selectedCollection) && documents.length === 0
   const totalDocuments = documentsData?.total || 0
   const totalPages = Math.ceil(totalDocuments / PAGE_SIZE)
 
@@ -563,7 +579,11 @@ export default function DatabasePage() {
                 ))
               ) : (
                 <p className="text-sm text-gray-500 dark:text-gray-400 text-center py-4">
-                  {searchTerm ? 'No matching documents' : 'No documents'}
+                  {searchTerm
+                    ? 'No matching documents'
+                    : isEmptyNewCollection
+                      ? 'Created, but empty. The database does not list or return a collection until something writes to it, so this will not appear after a reload until it holds a document.'
+                      : 'No documents'}
                 </p>
               )}
             </div>