feat(smartdb): add operation log APIs, point-in-time revert support, and a web-based debug dashboard
This commit is contained in:
@@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||
use bson::Document;
|
||||
use dashmap::DashMap;
|
||||
use rustdb_index::IndexEngine;
|
||||
use rustdb_storage::StorageAdapter;
|
||||
use rustdb_storage::{OpLog, StorageAdapter};
|
||||
use rustdb_txn::{SessionEngine, TransactionEngine};
|
||||
|
||||
/// Shared command execution context, passed to all handlers.
|
||||
@@ -20,6 +20,8 @@ pub struct CommandContext {
|
||||
pub cursors: Arc<DashMap<i64, CursorState>>,
|
||||
/// Server start time (for uptime reporting).
|
||||
pub start_time: std::time::Instant,
|
||||
/// Operation log for point-in-time replay.
|
||||
pub oplog: Arc<OpLog>,
|
||||
}
|
||||
|
||||
/// State of an open cursor from a find or aggregate command.
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::collections::HashSet;
|
||||
|
||||
use bson::{doc, Bson, Document};
|
||||
use rustdb_query::QueryMatcher;
|
||||
use rustdb_storage::OpType;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::context::CommandContext;
|
||||
@@ -171,6 +172,16 @@ async fn delete_matching(
|
||||
.await
|
||||
.map_err(|e| CommandError::StorageError(e.to_string()))?;
|
||||
|
||||
// Record in oplog.
|
||||
ctx.oplog.append(
|
||||
OpType::Delete,
|
||||
db,
|
||||
coll,
|
||||
&id_str,
|
||||
None,
|
||||
Some(doc.clone()),
|
||||
);
|
||||
|
||||
// Update index engine.
|
||||
if let Some(mut engine) = ctx.indexes.get_mut(ns_key) {
|
||||
engine.on_delete(doc);
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::collections::HashMap;
|
||||
|
||||
use bson::{doc, oid::ObjectId, Bson, Document};
|
||||
use rustdb_index::IndexEngine;
|
||||
use rustdb_storage::OpType;
|
||||
use tracing::{debug, warn};
|
||||
|
||||
use crate::context::CommandContext;
|
||||
@@ -63,7 +64,17 @@ pub async fn handle(
|
||||
|
||||
// Attempt storage insert.
|
||||
match ctx.storage.insert_one(db, coll, doc.clone()).await {
|
||||
Ok(_id_str) => {
|
||||
Ok(id_str) => {
|
||||
// Record in oplog.
|
||||
ctx.oplog.append(
|
||||
OpType::Insert,
|
||||
db,
|
||||
coll,
|
||||
&id_str,
|
||||
Some(doc.clone()),
|
||||
None,
|
||||
);
|
||||
|
||||
// Update index engine.
|
||||
let mut engine = ctx
|
||||
.indexes
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::collections::HashSet;
|
||||
use bson::{doc, oid::ObjectId, Bson, Document};
|
||||
use rustdb_index::IndexEngine;
|
||||
use rustdb_query::{QueryMatcher, UpdateEngine, sort_documents, apply_projection};
|
||||
use rustdb_storage::OpType;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::context::CommandContext;
|
||||
@@ -151,7 +152,17 @@ async fn handle_update(
|
||||
|
||||
// Insert the new document.
|
||||
match ctx.storage.insert_one(db, coll, updated.clone()).await {
|
||||
Ok(_) => {
|
||||
Ok(id_str) => {
|
||||
// Record upsert in oplog as an insert.
|
||||
ctx.oplog.append(
|
||||
OpType::Insert,
|
||||
db,
|
||||
coll,
|
||||
&id_str,
|
||||
Some(updated.clone()),
|
||||
None,
|
||||
);
|
||||
|
||||
// Update index.
|
||||
let mut engine = ctx
|
||||
.indexes
|
||||
@@ -212,6 +223,16 @@ async fn handle_update(
|
||||
.await
|
||||
{
|
||||
Ok(()) => {
|
||||
// Record in oplog.
|
||||
ctx.oplog.append(
|
||||
OpType::Update,
|
||||
db,
|
||||
coll,
|
||||
&id_str,
|
||||
Some(updated_doc.clone()),
|
||||
Some(matched_doc.clone()),
|
||||
);
|
||||
|
||||
// Update index.
|
||||
if let Some(mut engine) = ctx.indexes.get_mut(&ns_key) {
|
||||
let _ = engine.on_update(matched_doc, &updated_doc);
|
||||
@@ -362,6 +383,16 @@ async fn handle_find_and_modify(
|
||||
let id_str = extract_id_string(doc);
|
||||
ctx.storage.delete_by_id(db, coll, &id_str).await?;
|
||||
|
||||
// Record in oplog.
|
||||
ctx.oplog.append(
|
||||
OpType::Delete,
|
||||
db,
|
||||
coll,
|
||||
&id_str,
|
||||
None,
|
||||
Some(doc.clone()),
|
||||
);
|
||||
|
||||
// Update index.
|
||||
if let Some(mut engine) = ctx.indexes.get_mut(&ns_key) {
|
||||
engine.on_delete(doc);
|
||||
@@ -418,6 +449,16 @@ async fn handle_find_and_modify(
|
||||
.update_by_id(db, coll, &id_str, updated_doc.clone())
|
||||
.await?;
|
||||
|
||||
// Record in oplog.
|
||||
ctx.oplog.append(
|
||||
OpType::Update,
|
||||
db,
|
||||
coll,
|
||||
&id_str,
|
||||
Some(updated_doc.clone()),
|
||||
Some(original_doc.clone()),
|
||||
);
|
||||
|
||||
// Update index.
|
||||
if let Some(mut engine) = ctx.indexes.get_mut(&ns_key) {
|
||||
let _ = engine.on_update(&original_doc, &updated_doc);
|
||||
@@ -464,10 +505,20 @@ async fn handle_find_and_modify(
|
||||
updated_doc.get("_id").unwrap().clone()
|
||||
};
|
||||
|
||||
ctx.storage
|
||||
let inserted_id_str = ctx.storage
|
||||
.insert_one(db, coll, updated_doc.clone())
|
||||
.await?;
|
||||
|
||||
// Record upsert in oplog as an insert.
|
||||
ctx.oplog.append(
|
||||
OpType::Insert,
|
||||
db,
|
||||
coll,
|
||||
&inserted_id_str,
|
||||
Some(updated_doc.clone()),
|
||||
None,
|
||||
);
|
||||
|
||||
// Update index.
|
||||
{
|
||||
let mut engine = ctx
|
||||
|
||||
Reference in New Issue
Block a user