Add support for task expiration (#3546)

This commit is contained in:
Dustin J. Mitchell 2024-07-09 16:39:39 -04:00 committed by GitHub
parent 2bd609afe3
commit 213b9d3aee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 180 additions and 7 deletions

View file

@ -118,6 +118,7 @@ std::string configurationDefaults =
"json.array=1 # Enclose JSON output in [ ]\n"
"abbreviation.minimum=2 # Shortest allowed abbreviation\n"
"news.version= # Latest version highlights read by the user\n"
"expiration.on-sync=0 # Expire old tasks on sync\n"
"\n"
"# Dates\n"
"dateformat=Y-M-D # Preferred input and display date format\n"
@ -853,13 +854,8 @@ int Context::dispatch (std::string &out)
// The command know whether they need a GC.
if (c->needs_gc ())
{
run_gc = config.getBoolean ("gc");
tdb2.gc ();
}
else
{
run_gc = false;
}
// This is something that is only needed for write commands with no other
// filter processing.

View file

@ -98,7 +98,6 @@ public:
Hooks hooks {};
bool determine_color_use {true};
bool use_color {true};
bool run_gc {true};
bool verbosity_legacy {false};
std::set <std::string> verbosity {};
std::vector <std::string> headers {};

View file

@ -303,6 +303,7 @@ void TDB2::show_diff (
}
}
////////////////////////////////////////////////////////////////////////////////
void TDB2::gc ()
{
Timer timer;
@ -316,6 +317,12 @@ void TDB2::gc ()
Context::getContext ().time_gc_us += timer.total_us ();
}
////////////////////////////////////////////////////////////////////////////////
void TDB2::expire_tasks ()
{
replica.expire_tasks ();
}
////////////////////////////////////////////////////////////////////////////////
// Latest ID is that of the last pending task.
int TDB2::latest_id ()

View file

@ -56,6 +56,7 @@ public:
void get_changes (std::vector <Task>&);
void revert ();
void gc ();
void expire_tasks ();
int latest_id ();
// Generalized task accessors.

View file

@ -172,6 +172,7 @@ std::vector<NewsItem> NewsItem::all () {
std::vector<NewsItem> items;
version2_6_0(items);
version3_0_0(items);
version3_1_0(items);
return items;
}
@ -537,6 +538,27 @@ void NewsItem::version3_0_0 (std::vector<NewsItem>& items) {
items.push_back(sync);
}
void NewsItem::version3_1_0 (std::vector<NewsItem>& items) {
Version version("3.1.0");
NewsItem sync {
version,
/*title=*/"Purging and Expiring Tasks",
/*bg_title=*/"",
/*background=*/"",
/*punchline=*/
"Support for `task purge` has been restored, and new support added for automatically expiring\n"
"old tasks.\n\n"
/*update=*/
"The `task purge` command removes tasks entirely, in contrast to `task delete` which merely sets\n"
"the task status to 'Deleted'. This functionality existed in versions 2.x but was temporarily\n"
"removed in 3.0.\n\n"
"The new `expiration.on-sync` configuration parameter controls automatic expiration of old tasks.\n"
"An old task is one with status 'Deleted' that has not been modified in 180 days. This\n"
"functionality is optional and not enabled by default."
};
items.push_back(sync);
}
////////////////////////////////////////////////////////////////////////////////
int CmdNews::execute (std::string& output)
{

View file

@ -49,6 +49,7 @@ public:
static std::vector<NewsItem> all();
static void version2_6_0 (std::vector<NewsItem>&);
static void version3_0_0 (std::vector<NewsItem>&);
static void version3_1_0 (std::vector<NewsItem>&);
private:
NewsItem (

View file

@ -160,6 +160,7 @@ int CmdShow::execute (std::string& output)
" due"
" editor"
" exit.on.missing.db"
" expiration.on-sync"
" expressions"
" fontunderline"
" gc"

View file

@ -106,7 +106,12 @@ int CmdSync::execute (std::string& output)
<< '\n';
}
Context::getContext ().tdb2.sync(std::move(server), false);
Context &context = Context::getContext ();
context.tdb2.sync(std::move(server), false);
if (context.config.getBoolean ("expiration.on-sync")) {
context.tdb2.expire_tasks ();
}
output = out.str ();
return status;

View file

@ -144,6 +144,15 @@ tc::Task tc::Replica::import_task_with_uuid (const std::string &uuid)
return Task (tctask);
}
////////////////////////////////////////////////////////////////////////////////
void tc::Replica::expire_tasks ()
{
auto res = tc_replica_expire_tasks (&*inner);
if (res != TC_RESULT_OK) {
throw replica_error ();
}
}
////////////////////////////////////////////////////////////////////////////////
void tc::Replica::sync (Server server, bool avoid_snapshots)
{

View file

@ -92,6 +92,7 @@ namespace tc {
tc::Task new_task (Status status, const std::string &description);
tc::Task import_task_with_uuid (const std::string &uuid);
// TODO: struct TCTask *tc_replica_import_task_with_uuid(struct TCReplica *rep, struct TCUuid tcuuid);
void expire_tasks();
void sync(Server server, bool avoid_snapshots);
tc::ffi::TCReplicaOpList get_undo_ops ();
void commit_undo_ops (tc::ffi::TCReplicaOpList tc_undo_ops, int32_t *undone_out);

View file

@ -533,6 +533,31 @@ pub unsafe extern "C" fn tc_replica_sync(
)
}
#[ffizz_header::item]
#[ffizz(order = 902)]
/// Expire old, deleted tasks.
///
/// Expiration entails removal of tasks from the replica. Any modifications that occur after
/// the deletion (such as operations synchronized from other replicas) will do nothing.
///
/// Tasks are eligible for expiration when they have status Deleted and have not been modified
/// for 180 days (about six months). Note that completed tasks are not eligible.
///
/// ```c
/// EXTERN_C TCResult tc_replica_expire_tasks(struct TCReplica *rep);
/// ```
#[no_mangle]
pub unsafe extern "C" fn tc_replica_expire_tasks(rep: *mut TCReplica) -> TCResult {
wrap(
rep,
|rep| {
rep.expire_tasks()?;
Ok(TCResult::Ok)
},
TCResult::Error,
)
}
#[ffizz_header::item]
#[ffizz(order = 902)]
/// Return undo local operations until the most recent UndoPoint.

View file

@ -552,6 +552,15 @@ EXTERN_C TCResult tc_replica_commit_undo_ops(struct TCReplica *rep, TCReplicaOpL
// free the returned string.
EXTERN_C struct TCString tc_replica_error(struct TCReplica *rep);
// Expire old, deleted tasks.
//
// Expiration entails removal of tasks from the replica. Any modifications that occur after
// the deletion (such as operations synchronized from other replicas) will do nothing.
//
// Tasks are eligible for expiration when they have status Deleted and have not been modified
// for 180 days (about six months). Note that completed tasks are not eligible.
EXTERN_C TCResult tc_replica_expire_tasks(struct TCReplica *rep);
// Get an existing task by its UUID.
//
// Returns NULL when the task does not exist, and on error. Consult tc_replica_error