daemon: store/ query layer — tasks + settings (prep for the download.add vertical slice)
The read/write surface the dispatcher handlers need, so download.add persists and download.list / download.get project real rows when the engine lands. - store/tasks — TaskRow (1:1 with the schema), insert / get / remove / set_state / count, and list(filter, sort, offset, limit) that does all the WHERE / ORDER BY / LIMIT in SQL (M1 DoD: a 1000-row list never materialised client-side). Filter covers states / category / queue / case-insensitive filename+url substring / date range; sort is a whitelisted column + direction with NULLs last, default newest-first; the enum spellings in a state IN (...) come from proto::to_string, never from user text. to_summary() projects a row onto proto::TaskSummary including the flattened error block when the task failed / retry_wait / auto-paused. - store/settings — key -> JSON-text with a built-in default table mirroring Settings.schema.json / ADR 0012; get_raw / set_raw / overrides plus typed get_int / get_string / get_string_array for the governor config and saveTo.allowedRoots. Full settings.get/set wire projection lands with those handlers. - veloxd_store now links velox::proto + nlohmann_json for the projection. Test veloxd.store_tasks: insert/get round trip, PK duplicate rejected, the error-block projection, list total+paging+sort+every filter, set_state pause_reason clear-on-unpause, remove, and settings default vs override. ASan+UBSan and TSan clean; 34 daemon/cli tests green. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Upd9WhG9oppieig5nRDLig
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
// Read/write access to the `tasks` table, plus the projection onto the wire TaskSummary.
|
||||
// download.list does its filtering, sorting and paging here (M1 DoD: a 1000-row list under
|
||||
// 50 ms, never materialised client-side).
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "store/sqlite.hpp"
|
||||
#include "velox_proto.hpp"
|
||||
|
||||
namespace velox::daemon::store {
|
||||
|
||||
// One row of `tasks`, 1:1 with the schema. std::optional maps a NULL column.
|
||||
struct TaskRow {
|
||||
std::string task_id;
|
||||
std::string url;
|
||||
std::string save_dir;
|
||||
std::string filename;
|
||||
std::string state = "new";
|
||||
std::string start_mode = "auto";
|
||||
std::string created_at;
|
||||
|
||||
std::optional<std::string> effective_url;
|
||||
std::optional<std::string> category_id;
|
||||
std::optional<std::string> queue_id;
|
||||
std::optional<std::string> description;
|
||||
std::optional<std::string> pause_reason;
|
||||
std::optional<std::string> etag;
|
||||
std::optional<std::string> last_modified;
|
||||
std::optional<std::string> content_type;
|
||||
std::optional<std::string> last_try_at;
|
||||
std::optional<std::string> completed_at;
|
||||
std::optional<std::string> checksum_algo;
|
||||
std::optional<std::string> checksum_value;
|
||||
|
||||
std::optional<std::int64_t> size_bytes;
|
||||
std::int64_t downloaded_bytes = 0;
|
||||
bool resumable = false;
|
||||
|
||||
std::optional<std::int64_t> req_segments;
|
||||
std::int64_t eff_segments = 0;
|
||||
std::optional<std::int64_t> req_buffer_bytes;
|
||||
std::optional<std::int64_t> eff_buffer_bytes;
|
||||
std::optional<std::int64_t> queue_position;
|
||||
|
||||
std::optional<std::string> error_code;
|
||||
std::optional<std::string> error_message;
|
||||
std::optional<std::int64_t> error_http_status;
|
||||
std::optional<bool> error_retryable;
|
||||
std::optional<std::int64_t> error_attempt;
|
||||
std::optional<std::string> error_next_retry_at;
|
||||
};
|
||||
|
||||
class Tasks {
|
||||
public:
|
||||
explicit Tasks(Db& db) : db_(db) {}
|
||||
|
||||
DbResult<void> insert(const TaskRow& row);
|
||||
DbResult<std::optional<TaskRow>> get(std::string_view task_id);
|
||||
|
||||
struct Page {
|
||||
std::int64_t total = 0; // rows matching the filter, ignoring paging
|
||||
std::vector<TaskRow> rows;
|
||||
};
|
||||
DbResult<Page> list(const std::optional<velox::proto::TaskFilter>& filter,
|
||||
const std::optional<velox::proto::TaskSort>& sort, std::int64_t offset,
|
||||
std::int64_t limit);
|
||||
|
||||
// Move a task to `state`; `pause_reason` is written only when state == "paused"
|
||||
// (cleared otherwise). Returns false if there is no such task.
|
||||
DbResult<bool> set_state(std::string_view task_id, std::string_view state,
|
||||
const std::optional<std::string>& pause_reason);
|
||||
|
||||
DbResult<bool> remove(std::string_view task_id);
|
||||
DbResult<std::int64_t> count();
|
||||
|
||||
private:
|
||||
Db& db_;
|
||||
};
|
||||
|
||||
// Project a row onto the wire type. `state` and `error.code` strings are assumed valid
|
||||
// (the CHECK constraints and the state machine keep them so).
|
||||
velox::proto::TaskSummary to_summary(const TaskRow& row);
|
||||
|
||||
} // namespace velox::daemon::store
|
||||
Reference in New Issue
Block a user