From f098b60504156854edaed332d8ff7568d9bcde26 Mon Sep 17 00:00:00 2001 From: Bill Katz Date: Tue, 4 Jun 2024 23:32:48 -0400 Subject: [PATCH] allow deletion of data instance by data UUID --- datastore/datastore.go | 26 +++++++------ datastore/repo_local.go | 83 ++++++++++++++++++++++++----------------- server/rpc.go | 27 +++++++------- 3 files changed, 75 insertions(+), 61 deletions(-) diff --git a/datastore/datastore.go b/datastore/datastore.go index 878419b5..252fe01e 100644 --- a/datastore/datastore.go +++ b/datastore/datastore.go @@ -406,28 +406,30 @@ func GetDataByVersionName(v dvid.VersionID, name dvid.InstanceName) (DataService return manager.getDataByVersionName(v, name) } -// DeleteDataByName returns a data service given an instance name and UUID. +// DeleteDataByName deletes a data service given an instance name and UUID. func DeleteDataByName(uuid dvid.UUID, name dvid.InstanceName, passcode string) error { - if manager == nil { - return ErrManagerNotInitialized + data, err := GetDataByUUIDName(uuid, name) + if err != nil { + return err } - return manager.deleteDataByName(uuid, name, passcode) + return manager.deleteData(data, passcode) } -// RenameData renames a data service given an old instance name and UUID. -func RenameData(uuid dvid.UUID, oldname, newname dvid.InstanceName, passcode string) error { - if manager == nil { - return ErrManagerNotInitialized +// DeleteDataByDataUUID deletes a data service given a data UUID +func DeleteDataByDataUUID(dataUUID dvid.UUID, passcode string) error { + data, err := GetDataByDataUUID(dataUUID) + if err != nil { + return err } - return manager.renameDataByName(uuid, oldname, newname, passcode) + return manager.deleteData(data, passcode) } -// DeleteDataByVersion returns a data service given an instance name and UUID. -func DeleteDataByVersion(v dvid.VersionID, name dvid.InstanceName, passcode string) error { +// RenameData renames a data service given an old instance name and UUID. +func RenameData(uuid dvid.UUID, oldname, newname dvid.InstanceName, passcode string) error { if manager == nil { return ErrManagerNotInitialized } - return manager.deleteDataByVersion(v, name, passcode) + return manager.renameDataByName(uuid, oldname, newname, passcode) } func ModifyDataConfigByName(uuid dvid.UUID, name dvid.InstanceName, c dvid.Config) error { diff --git a/datastore/repo_local.go b/datastore/repo_local.go index 5ea20f00..8fbcbade 100644 --- a/datastore/repo_local.go +++ b/datastore/repo_local.go @@ -733,7 +733,7 @@ func (m *repoManager) loadVersion0() error { } for name, data := range r.data { if data.IsDeleted() { - if err := r.deleteData(name); err != nil { + if err := r.deleteDataByName(name); err != nil { dvid.TimeCriticalf("tried to restart deletion of data %q but failed: %v\n", name, err) } } @@ -2320,20 +2320,19 @@ func (m *repoManager) renameDataByName(uuid dvid.UUID, oldname, newname dvid.Ins // deleteDataByName deletes all data associated with the data instance and removes // it from the Repo. -func (m *repoManager) deleteDataByName(uuid dvid.UUID, name dvid.InstanceName, passcode string) error { - r, err := m.repoFromUUID(uuid) +func (m *repoManager) deleteData(data DataService, passcode string) error { + r, err := m.repoFromUUID(data.RootUUID()) if err != nil { return err } - return r.deleteDataWithPasscode(name, passcode) -} + if r.passcodeOK(passcode) { + data.SetDeleted(true) + go r.deleteData(data) + } else { + return fmt.Errorf("incorrect passcode for repo %s", r.uuid) -func (m *repoManager) deleteDataByVersion(v dvid.VersionID, name dvid.InstanceName, passcode string) error { - r, err := m.repoFromVersion(v) - if err != nil { - return err } - return r.deleteDataWithPasscode(name, passcode) + return nil } // modifyData modifies preexisting Data within a Repo. Settings can be passed @@ -2427,6 +2426,37 @@ func (r *repoT) branchHeads() map[string]dvid.UUID { return branchToUUID } +// For all data tiers of storage, remove data kv pairs associated with this data instance. +// This can be called asynchronously since it can be time-consuming. +func (r *repoT) deleteData(data DataService) { + if err := storage.DeleteDataInstance(data); err != nil { + dvid.Errorf("Error trying to do async data instance deletion: %v\n", err) + } + + // Delete entries in the sync graph if this data needs to be synced with another data instance. + _, syncable := data.(Syncer) + if syncable { + r.deleteSyncGraph(data, false) + } + + // Remove this data instance from the repository and persist. + r.Lock() + tm := time.Now() + r.updated = tm + msg := fmt.Sprintf("Delete data instance '%s' of type '%s'", data.DataName(), data.TypeName()) + message := fmt.Sprintf("%s %s", tm.Format(time.RFC3339), msg) + r.log = append(r.log, message) + delete(r.data, data.DataName()) + r.Unlock() + r.save() +} + +func (r *repoT) passcodeOK(passcode string) bool { + r.RLock() + defer r.RUnlock() + return r.passcode == "" || r.passcode == passcode +} + func (r *repoT) deleteDataWithPasscode(name dvid.InstanceName, passcode string) error { r.RLock() if r.passcode != "" && r.passcode != passcode { @@ -2434,10 +2464,10 @@ func (r *repoT) deleteDataWithPasscode(name dvid.InstanceName, passcode string) return fmt.Errorf("incorrect passcode for repo %s", r.uuid) } r.RUnlock() - return r.deleteData(name) + return r.deleteDataByName(name) } -func (r *repoT) deleteData(name dvid.InstanceName) error { +func (r *repoT) deleteDataByName(name dvid.InstanceName) error { r.RLock() data, found := r.data[name] r.RUnlock() @@ -2446,30 +2476,13 @@ func (r *repoT) deleteData(name dvid.InstanceName) error { } data.SetDeleted(true) - // For all data tiers of storage, remove data kv pairs associated with this instance id. - go func() { - if err := storage.DeleteDataInstance(data); err != nil { - dvid.Errorf("Error trying to do async data instance deletion: %v\n", err) - } - - // Delete entries in the sync graph if this data needs to be synced with another data instance. - _, syncable := data.(Syncer) - if syncable { - r.deleteSyncGraph(data, false) - } - - // Remove this data instance from the repository and persist. - r.Lock() - tm := time.Now() - r.updated = tm - msg := fmt.Sprintf("Delete data instance '%s' of type '%s'", name, data.TypeName()) - message := fmt.Sprintf("%s %s", tm.Format(time.RFC3339), msg) - r.log = append(r.log, message) - delete(r.data, name) - r.Unlock() - r.save() - }() + go r.deleteData(data) + return nil +} +func (r *repoT) deleteDataByDataUUID(data DataService) error { + data.SetDeleted(true) + go r.deleteData(data) return nil } diff --git a/server/rpc.go b/server/rpc.go index d356e6bf..f1cf9531 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -56,9 +56,10 @@ DANGEROUS COMMANDS (only available via command line) Deletes an entire repo with the given root UUID. - repo delete + repo delete - Delete the given data instance. + Delete the given data instance. First checks by data UUID, and if a + data instance is not found, checks by name. repo make-master @@ -674,24 +675,22 @@ func handleCommand(cmd *datastore.Request) (reply *datastore.Response, err error */ case "delete": - var dataname, passcode string - cmd.CommandArgs(3, &dataname, &passcode) + var dataId string // either data name or data UUID + var passcode string + cmd.CommandArgs(3, &dataId, &passcode) - // Make sure this instance exists. - if _, err = datastore.GetDataByUUIDName(uuid, dvid.InstanceName(dataname)); err != nil { - err = fmt.Errorf("Error trying to delete %q for UUID %s: %v", dataname, uuid, err) - return + // Try to delete a data UUID first, and if doesn't work, try by name. + if err = datastore.DeleteDataByDataUUID(dvid.UUID(dataId), passcode); err != nil { + err = datastore.DeleteDataByName(uuid, dvid.InstanceName(dataId), passcode) } - - // Do the deletion. Under hood, modifies metadata immediately and launches async k/v deletion. - if err = datastore.DeleteDataByName(uuid, dvid.InstanceName(dataname), passcode); err != nil { - err = fmt.Errorf("Error deleting data instance %q: %v", dataname, err) + if err != nil { + err = fmt.Errorf("error deleting data instance %q: %v", dataId, err) return } - reply.Text = fmt.Sprintf("Started deletion of data instance %q from repo with root %s\n", dataname, uuid) + reply.Text = fmt.Sprintf("Started deletion of data instance %q from repo with root %s\n", dataId, uuid) default: - err = fmt.Errorf("Unknown command: %q", cmd) + err = fmt.Errorf("unknown command: %q", cmd) return }