diff --git a/internal/httpapi/library_capture.go b/internal/httpapi/library_capture.go index 08e7217..76fe671 100644 --- a/internal/httpapi/library_capture.go +++ b/internal/httpapi/library_capture.go @@ -97,7 +97,10 @@ func libraryCaptureEnqueue(w http.ResponseWriter, r *http.Request, config Config } } } - requestedID := fmt.Sprintf("library-capture-%s-%d", request.Scope, now.UnixNano()) + // 队列作业 id 落进 jobs.id(varchar(36)):原来拼 "library-capture--" + // 是 45 个字符,真实请求 100% 被 PG 拒(value too long)——只是页面一直停在 + // "未接线"上,没人走到这里。这里按列宽拼短 id,并留出 scope 全称。 + requestedID := fmt.Sprintf("libcap-%s-%d", request.Scope, now.UnixNano()) // The idempotency key is the capture job id: the same logical capture must map // to one queue row. Binding the scope into the key would silently create a // second job instead of reporting a conflicting request, so the conflict is diff --git a/internal/httpapi/library_capture_test.go b/internal/httpapi/library_capture_test.go index 060d56f..9b7a41d 100644 --- a/internal/httpapi/library_capture_test.go +++ b/internal/httpapi/library_capture_test.go @@ -86,6 +86,27 @@ func TestLibraryCaptureRejectsInvalidRequest(t *testing.T) { } } +func TestLibraryCaptureQueueIDFitsTheColumn(t *testing.T) { + // jobs.id 是 varchar(36):拼长的 id 会在真实 PG 上 500(value too long)。 + // 页面长期停在"未接线",这条从没被真实请求覆盖过,所以用测试锁住列宽。 + for _, scope := range []string{"internal", "external"} { + store := &fakeJobStore{enqueueEchoRequestedID: true} + recorder := httptest.NewRecorder() + libraryCaptureEnqueue(recorder, captureRequest(`{"scope":"`+scope+`"}`), + Config{Store: store, LibraryAgentSocket: "/tmp/agent.sock"}) + if recorder.Code != http.StatusAccepted { + t.Fatalf("%s status=%d body=%s", scope, recorder.Code, recorder.Body.String()) + } + enqueued := store.enqueued + if len(enqueued.ID) > 36 { + t.Fatalf("%s 队列作业 id 超长(%d):%q", scope, len(enqueued.ID), enqueued.ID) + } + if !strings.HasPrefix(enqueued.ID, "libcap-"+scope+"-") { + t.Fatalf("%s 队列作业 id 形状变了:%q", scope, enqueued.ID) + } + } +} + func TestLibraryCaptureJobsAsksStoreForItsOwnType(t *testing.T) { // The global queue is busy with unrelated traffic; the capture job must still // be visible because the query is type-scoped, not filtered after the fact.