Files
gitlore/src/ingestion/discussion_queue.rs
Taylor Eernisse 65583ed5d6 refactor: Remove redundant doc comments throughout codebase
Removes module-level doc comments (//! lines) and excessive inline doc
comments that were duplicating information already evident from:
- Function/struct names (self-documenting code)
- Type signatures (the what is clear from types)
- Implementation context (the how is clear from code)

Affected modules:
- cli/* - Removed command descriptions duplicating clap help text
- core/* - Removed module headers and obvious function docs
- documents/* - Removed extractor/regenerator/truncation docs
- embedding/* - Removed pipeline and chunking docs
- gitlab/* - Removed client and transformer docs (kept type definitions)
- ingestion/* - Removed orchestrator and ingestion docs
- search/* - Removed FTS and vector search docs

Philosophy: Code should be self-documenting. Comments should explain
"why" (business decisions, non-obvious constraints) not "what" (which
the code itself shows). This change reduces noise and maintenance burden
while keeping the codebase just as understandable.

Retains comments for:
- Non-obvious business logic
- Important safety invariants
- Complex algorithm explanations
- Public API boundaries where generated docs matter

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 00:04:32 -05:00

281 lines
8.7 KiB
Rust

use rusqlite::Connection;
use crate::core::backoff::compute_next_attempt_at;
use crate::core::error::Result;
use crate::core::time::now_ms;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NoteableType {
Issue,
MergeRequest,
}
impl NoteableType {
pub fn as_str(&self) -> &'static str {
match self {
Self::Issue => "Issue",
Self::MergeRequest => "MergeRequest",
}
}
pub fn parse(s: &str) -> Option<Self> {
match s {
"Issue" => Some(Self::Issue),
"MergeRequest" => Some(Self::MergeRequest),
_ => None,
}
}
}
pub struct PendingFetch {
pub project_id: i64,
pub noteable_type: NoteableType,
pub noteable_iid: i64,
pub attempt_count: i32,
}
pub fn queue_discussion_fetch(
conn: &Connection,
project_id: i64,
noteable_type: NoteableType,
noteable_iid: i64,
) -> Result<()> {
conn.execute(
"INSERT INTO pending_discussion_fetches (project_id, noteable_type, noteable_iid, queued_at)
VALUES (?1, ?2, ?3, ?4)
ON CONFLICT(project_id, noteable_type, noteable_iid) DO UPDATE SET
queued_at = excluded.queued_at,
attempt_count = 0,
last_attempt_at = NULL,
last_error = NULL,
next_attempt_at = NULL",
rusqlite::params![project_id, noteable_type.as_str(), noteable_iid, now_ms()],
)?;
Ok(())
}
pub fn get_pending_fetches(conn: &Connection, limit: usize) -> Result<Vec<PendingFetch>> {
let now = now_ms();
let mut stmt = conn.prepare(
"SELECT project_id, noteable_type, noteable_iid, attempt_count
FROM pending_discussion_fetches
WHERE next_attempt_at IS NULL OR next_attempt_at <= ?1
ORDER BY queued_at ASC
LIMIT ?2",
)?;
let rows = stmt
.query_map(rusqlite::params![now, limit as i64], |row| {
Ok((
row.get::<_, i64>(0)?,
row.get::<_, String>(1)?,
row.get::<_, i64>(2)?,
row.get::<_, i32>(3)?,
))
})?
.collect::<std::result::Result<Vec<_>, _>>()?;
let mut results = Vec::with_capacity(rows.len());
for (project_id, nt_str, noteable_iid, attempt_count) in rows {
let noteable_type = NoteableType::parse(&nt_str).ok_or_else(|| {
crate::core::error::LoreError::Other(format!(
"Invalid noteable_type in pending_discussion_fetches: {}",
nt_str
))
})?;
results.push(PendingFetch {
project_id,
noteable_type,
noteable_iid,
attempt_count,
});
}
Ok(results)
}
pub fn complete_fetch(
conn: &Connection,
project_id: i64,
noteable_type: NoteableType,
noteable_iid: i64,
) -> Result<()> {
conn.execute(
"DELETE FROM pending_discussion_fetches
WHERE project_id = ?1 AND noteable_type = ?2 AND noteable_iid = ?3",
rusqlite::params![project_id, noteable_type.as_str(), noteable_iid],
)?;
Ok(())
}
pub fn record_fetch_error(
conn: &Connection,
project_id: i64,
noteable_type: NoteableType,
noteable_iid: i64,
error: &str,
) -> Result<()> {
let now = now_ms();
let attempt_count: i64 = conn.query_row(
"SELECT attempt_count FROM pending_discussion_fetches
WHERE project_id = ?1 AND noteable_type = ?2 AND noteable_iid = ?3",
rusqlite::params![project_id, noteable_type.as_str(), noteable_iid],
|row| row.get(0),
)?;
let new_attempt = attempt_count + 1;
let next_at = compute_next_attempt_at(now, new_attempt);
conn.execute(
"UPDATE pending_discussion_fetches SET
attempt_count = ?1,
last_attempt_at = ?2,
last_error = ?3,
next_attempt_at = ?4
WHERE project_id = ?5 AND noteable_type = ?6 AND noteable_iid = ?7",
rusqlite::params![
new_attempt,
now,
error,
next_at,
project_id,
noteable_type.as_str(),
noteable_iid
],
)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fn setup_db() -> Connection {
let conn = Connection::open_in_memory().unwrap();
conn.execute_batch("
CREATE TABLE projects (
id INTEGER PRIMARY KEY,
gitlab_project_id INTEGER UNIQUE NOT NULL,
path_with_namespace TEXT NOT NULL,
default_branch TEXT,
web_url TEXT,
created_at INTEGER,
updated_at INTEGER,
raw_payload_id INTEGER
);
INSERT INTO projects (id, gitlab_project_id, path_with_namespace) VALUES (1, 100, 'group/project');
CREATE TABLE pending_discussion_fetches (
project_id INTEGER NOT NULL REFERENCES projects(id),
noteable_type TEXT NOT NULL,
noteable_iid INTEGER NOT NULL,
queued_at INTEGER NOT NULL,
attempt_count INTEGER NOT NULL DEFAULT 0,
last_attempt_at INTEGER,
last_error TEXT,
next_attempt_at INTEGER,
PRIMARY KEY(project_id, noteable_type, noteable_iid)
);
CREATE INDEX idx_pending_discussions_next_attempt ON pending_discussion_fetches(next_attempt_at);
").unwrap();
conn
}
#[test]
fn test_queue_and_get() {
let conn = setup_db();
queue_discussion_fetch(&conn, 1, NoteableType::Issue, 42).unwrap();
let fetches = get_pending_fetches(&conn, 100).unwrap();
assert_eq!(fetches.len(), 1);
assert_eq!(fetches[0].project_id, 1);
assert_eq!(fetches[0].noteable_type, NoteableType::Issue);
assert_eq!(fetches[0].noteable_iid, 42);
assert_eq!(fetches[0].attempt_count, 0);
}
#[test]
fn test_requeue_resets_backoff() {
let conn = setup_db();
queue_discussion_fetch(&conn, 1, NoteableType::Issue, 42).unwrap();
record_fetch_error(&conn, 1, NoteableType::Issue, 42, "network error").unwrap();
let attempt: i32 = conn
.query_row(
"SELECT attempt_count FROM pending_discussion_fetches WHERE noteable_iid = 42",
[],
|r| r.get(0),
)
.unwrap();
assert_eq!(attempt, 1);
queue_discussion_fetch(&conn, 1, NoteableType::Issue, 42).unwrap();
let attempt: i32 = conn
.query_row(
"SELECT attempt_count FROM pending_discussion_fetches WHERE noteable_iid = 42",
[],
|r| r.get(0),
)
.unwrap();
assert_eq!(attempt, 0);
}
#[test]
fn test_backoff_respected() {
let conn = setup_db();
queue_discussion_fetch(&conn, 1, NoteableType::Issue, 42).unwrap();
conn.execute(
"UPDATE pending_discussion_fetches SET next_attempt_at = 9999999999999 WHERE noteable_iid = 42",
[],
).unwrap();
let fetches = get_pending_fetches(&conn, 100).unwrap();
assert!(fetches.is_empty());
}
#[test]
fn test_complete_removes() {
let conn = setup_db();
queue_discussion_fetch(&conn, 1, NoteableType::Issue, 42).unwrap();
complete_fetch(&conn, 1, NoteableType::Issue, 42).unwrap();
let count: i64 = conn
.query_row("SELECT COUNT(*) FROM pending_discussion_fetches", [], |r| {
r.get(0)
})
.unwrap();
assert_eq!(count, 0);
}
#[test]
fn test_error_increments_attempts() {
let conn = setup_db();
queue_discussion_fetch(&conn, 1, NoteableType::MergeRequest, 10).unwrap();
record_fetch_error(&conn, 1, NoteableType::MergeRequest, 10, "timeout").unwrap();
let (attempt, error): (i32, Option<String>) = conn.query_row(
"SELECT attempt_count, last_error FROM pending_discussion_fetches WHERE noteable_iid = 10",
[], |r| Ok((r.get(0)?, r.get(1)?)),
).unwrap();
assert_eq!(attempt, 1);
assert_eq!(error, Some("timeout".to_string()));
let next_at: Option<i64> = conn
.query_row(
"SELECT next_attempt_at FROM pending_discussion_fetches WHERE noteable_iid = 10",
[],
|r| r.get(0),
)
.unwrap();
assert!(next_at.is_some());
}
#[test]
fn test_noteable_type_parse() {
assert_eq!(NoteableType::parse("Issue"), Some(NoteableType::Issue));
assert_eq!(
NoteableType::parse("MergeRequest"),
Some(NoteableType::MergeRequest)
);
assert_eq!(NoteableType::parse("invalid"), None);
}
}