Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions java-ecosystem/libs/core/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,6 @@
to org.hibernate.orm.core, spring.beans, spring.context, spring.core;
opens org.rostilos.codecrow.core.persistence.repository.qadoc
to spring.core, spring.beans, spring.context;

exports org.rostilos.codecrow.core.service.qadoc;
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ public class RagIndexStatus {
@Column(name = "chunk_count")
private Integer chunkCount;

/** Durable producer identity used to keep stale recovery from touching a newer run. */
@Column(name = "active_job_id")
private Long activeJobId;

@PreUpdate
protected void onUpdate() {
updatedAt = OffsetDateTime.now();
Expand Down Expand Up @@ -198,4 +202,12 @@ public Integer getChunkCount() {
public void setChunkCount(Integer chunkCount) {
this.chunkCount = chunkCount;
}

public Long getActiveJobId() {
return activeJobId;
}

public void setActiveJobId(Long activeJobId) {
this.activeJobId = activeJobId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ public class RagIndexOperation {
@Column(name = "job_id")
private Long jobId;

/** Exact analysis-lock owner captured for safe abandoned-producer cleanup. */
@Column(name = "analysis_lock_key", length = 500)
private String analysisLockKey;

@Column(name = "attempt_count", nullable = false)
private int attemptCount;

Expand Down Expand Up @@ -124,6 +128,8 @@ public void heartbeat() {
public void setGeneration(RagBranchIndexGeneration generation) { this.generation = generation; }
public Long getJobId() { return jobId; }
public void setJobId(Long jobId) { this.jobId = jobId; }
public String getAnalysisLockKey() { return analysisLockKey; }
public void setAnalysisLockKey(String analysisLockKey) { this.analysisLockKey = analysisLockKey; }
public int getAttemptCount() { return attemptCount; }
public void setAttemptCount(int attemptCount) { this.attemptCount = attemptCount; }
public OffsetDateTime getCreatedAt() { return createdAt; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.rostilos.codecrow.core.persistence.repository.analysis;

import jakarta.persistence.LockModeType;
import org.rostilos.codecrow.core.model.analysis.RagIndexStatus;
import org.rostilos.codecrow.core.model.analysis.RagIndexingStatus;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
Expand All @@ -15,6 +17,10 @@ public interface RagIndexStatusRepository extends JpaRepository<RagIndexStatus,

Optional<RagIndexStatus> findByProjectId(Long projectId);

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT r FROM RagIndexStatus r WHERE r.project.id = :projectId")
Optional<RagIndexStatus> findByProjectIdForUpdate(@Param("projectId") Long projectId);

Optional<RagIndexStatus> findByWorkspaceNameAndProjectName(String workspaceName, String projectName);

List<RagIndexStatus> findByStatus(RagIndexingStatus status);
Expand All @@ -37,4 +43,3 @@ List<RagIndexStatus> findByWorkspaceAndStatus(@Param("workspace") String workspa

void deleteByProjectId(Long projectId);
}

Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@
@Repository
public interface RagBranchIndexRepository extends JpaRepository<RagBranchIndex, Long> {

interface OperatorAliasCandidate {
Long getProjectId();
String getWorkspaceName();
String getProjectNamespace();
String getBranchName();
String getRevision();
String getCollectionName();
RagBranchIndexKind getIndexKind();
}

Optional<RagBranchIndex> findByProjectIdAndBranchName(Long projectId, String branchName);

@Lock(LockModeType.PESSIMISTIC_WRITE)
Expand Down Expand Up @@ -47,4 +57,26 @@ Optional<RagBranchIndex> findByProjectIdAndBranchNameForUpdate(

@Query("SELECT b.branchName FROM RagBranchIndex b WHERE b.project.id = :projectId")
List<String> findBranchNamesByProjectId(@Param("projectId") Long projectId);

/**
* Reads the immutable values needed by optional operator-alias repair.
* Returning a scalar projection lets the database transaction finish
* before the caller performs any potentially slow RAG/Qdrant request.
*/
@Query("""
SELECT b.project.id AS projectId,
b.project.workspace.name AS workspaceName,
b.project.namespace AS projectNamespace,
b.branchName AS branchName,
b.activeGeneration.revision AS revision,
b.activeGeneration.collectionName AS collectionName,
b.indexKind AS indexKind
FROM RagBranchIndex b
WHERE b.activeGeneration IS NOT NULL
AND b.indexKind IN (
org.rostilos.codecrow.core.model.rag.RagBranchIndexKind.PRIMARY,
org.rostilos.codecrow.core.model.rag.RagBranchIndexKind.DURABLE
)
""")
List<OperatorAliasCandidate> findOperatorAliasCandidates();
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package org.rostilos.codecrow.core.persistence.repository.rag;

import jakarta.persistence.LockModeType;
import org.rostilos.codecrow.core.model.rag.RagIndexOperation;
import org.rostilos.codecrow.core.model.rag.RagIndexOperationStatus;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.time.OffsetDateTime;
Expand All @@ -14,7 +18,50 @@ public interface RagIndexOperationRepository extends JpaRepository<RagIndexOpera

Optional<RagIndexOperation> findByProjectIdAndOperationKey(Long projectId, String operationKey);

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT o FROM RagIndexOperation o WHERE o.id = :operationId")
Optional<RagIndexOperation> findByIdForUpdate(@Param("operationId") Long operationId);

List<RagIndexOperation> findByStatusInAndUpdatedAtBefore(
List<RagIndexOperationStatus> statuses,
OffsetDateTime updatedBefore);

boolean existsByProjectIdAndBranchNameAndStatusIn(
Long projectId,
String branchName,
List<RagIndexOperationStatus> statuses);

/**
* Finds already-failed operations whose durable projections still say the
* producer is active. This repairs drift created by older recovery code or
* by a partial recovery failure on the next scan.
*/
@Query(value = """
SELECT o.*
FROM rag_index_operation o
WHERE o.status = 'FAILED'
AND (
EXISTS (
SELECT 1 FROM job j
WHERE j.id = o.job_id
AND j.status IN ('PENDING', 'QUEUED', 'RUNNING', 'WAITING')
)
OR EXISTS (
SELECT 1 FROM rag_index_status s
WHERE s.project_id = o.project_id
AND s.indexed_branch = o.branch_name
AND s.status IN ('INDEXING', 'UPDATING')
AND (s.active_job_id IS NULL OR s.active_job_id = o.job_id)
)
OR EXISTS (
SELECT 1 FROM analysis_lock l
WHERE l.project_id = o.project_id
AND l.branch_name = o.branch_name
AND l.analysis_type = 'RAG_INDEXING'
AND l.commit_hash IS NOT DISTINCT FROM o.to_revision
)
)
ORDER BY o.completed_at DESC, o.id DESC
""", nativeQuery = true)
List<RagIndexOperation> findFailedOperationsWithActiveProjections();
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ public Optional<QaDocDocument> findLatestDocument(Long projectId, Long prNumber)
return qaDocDocumentRepository.findByProjectIdAndPrNumber(projectId, prNumber);
}

@Transactional(readOnly = true)
public Optional<QaDocDocument> findDocumentById(Long documentId) {
if (documentId == null) {
return Optional.empty();
}
return qaDocDocumentRepository.findById(documentId);
}

@Transactional(readOnly = true)
public Optional<QaDocDocument> findLatestDocumentForTask(Long projectId,
String taskId,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.rostilos.codecrow.core.service.qadoc;

import java.util.List;

public record QaDocContent(
String overviewMarkdown,
List<QaDocTestCase> testCases,
String environmentMarkdown
) {
}
Loading
Loading