Skip to content

Commit 50cda04

Browse files
committed
volume.DeleteBlob
1 parent 2b3fcd4 commit 50cda04

5 files changed

Lines changed: 95 additions & 0 deletions

File tree

frontend/pages/views/IntegrityVerificationJobsView.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
IntegrityverificationjobClearProblems,
1919
IntegrityverificationjobResume,
2020
IntegrityverificationjobStop,
21+
VolumeDeleteBlob,
2122
VolumeVerifyIntegrity,
2223
} from 'generated/stoserver/stoservertypes_commands';
2324
import {
@@ -152,6 +153,14 @@ export default class IntegrityVerificationJobsView extends React.Component<
152153
/>
153154
);
154155

156+
const deleteBlob = (
157+
<CommandLink
158+
command={VolumeDeleteBlob(vol.Id, {
159+
disambiguation: vol.Label,
160+
})}
161+
/>
162+
);
163+
155164
const volumeMounted = this.props.mounts.some((m) => m.Volume === vol.Id && m.Online);
156165

157166
return (
@@ -199,6 +208,7 @@ export default class IntegrityVerificationJobsView extends React.Component<
199208
{completed && (
200209
<Dropdown>
201210
{startJob}
211+
{deleteBlob}
202212
{job.ErrorsFound > 0 && (
203213
<CommandLink
204214
command={IntegrityverificationjobClearProblems(job.Id)}
@@ -211,6 +221,7 @@ export default class IntegrityVerificationJobsView extends React.Component<
211221
<CommandLink command={IntegrityverificationjobResume(job.Id)} />
212222
<CommandLink command={IntegrityverificationjobStop(job.Id)} />
213223
{startJob}
224+
{deleteBlob}
214225
</Dropdown>
215226
)}
216227
</td>

pkg/stoserver/commandhandlersdatamanagement.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package stoserver
33
import (
44
"errors"
55
"fmt"
6+
"log/slog"
67
"slices"
78
"time"
89

@@ -210,6 +211,53 @@ func (c *cHandlers) VolumeMarkDataLost(cmd *stoservertypes.VolumeMarkDataLost, c
210211
})
211212
}
212213

214+
func (c *cHandlers) VolumeDeleteBlob(cmd *stoservertypes.VolumeDeleteBlob, ctx *command.Ctx) error {
215+
ref, err := stotypes.BlobRefFromHex(cmd.Ref)
216+
if err != nil {
217+
return err
218+
}
219+
220+
return c.db.Update(func(tx *bbolt.Tx) error {
221+
volToRemoveFrom, err := stodb.Read(tx).Volume(cmd.Id)
222+
if err != nil {
223+
return err
224+
}
225+
226+
blob, err := stodb.Read(tx).Blob(*ref)
227+
if err != nil {
228+
return err
229+
}
230+
231+
volumeRemoved := lo.Filter(blob.Volumes, func(volID int, _ int) bool { return volID != volToRemoveFrom.ID })
232+
233+
if removalHasNoChange := len(volumeRemoved) == len(blob.Volumes); removalHasNoChange {
234+
return fmt.Errorf("requested blob %s not found in volume %s", cmd.Ref, volToRemoveFrom.Label)
235+
}
236+
237+
if err := c.conf.DiskAccess.Delete(ctx.Ctx, *ref, volToRemoveFrom.ID); err != nil {
238+
// don't abort because blob may be missing in storage and we must still be able to do book-keeping changes
239+
// i.e. mark in metadata DB that volume no longer has blob.
240+
slog.Warn("failed to remove from underlying storage", "blob", ref.AsHex())
241+
}
242+
243+
volToRemoveFrom.BlobSizeTotal -= int64(blob.SizeOnDisk)
244+
volToRemoveFrom.BlobCount--
245+
246+
blob.Volumes = volumeRemoved
247+
248+
if err := stodb.VolumeRepository.Update(volToRemoveFrom, tx); err != nil {
249+
return err
250+
}
251+
252+
if err := stodb.BlobRepository.Update(blob, tx); err != nil {
253+
return err
254+
}
255+
256+
return nil
257+
})
258+
}
259+
260+
// even though collections are given as argument, only the blobs with problems are replicated to the target volume
213261
func (c *cHandlers) DatabaseReconcileReplicationPolicy(cmd *stoservertypes.DatabaseReconcileReplicationPolicy, ctx *command.Ctx) error {
214262
collIDs := *cmd.Collections
215263

pkg/stoserver/stodiskaccess/diskaccess.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,23 @@ func (d *Controller) BestVolumeID(volumeIDs []int) (int, error) {
268268
return lowestCostVolumeID, nil
269269
}
270270

271+
// deletes a blob from one volume
272+
func (d *Controller) Delete(ctx context.Context, ref stotypes.BlobRef, volumeID int) error {
273+
driver, err := d.driverFor(volumeID)
274+
if err != nil {
275+
return err
276+
}
277+
278+
if err := driver.RawDelete(ctx, ref); err != nil {
279+
return err
280+
}
281+
282+
// NOTE: not calling calling metadata store to record blob having been removed. right now we leave it to caller
283+
// in order for the caller (to let it itself decide on what to do with errors removing the blob from underlying storage)
284+
285+
return nil
286+
}
287+
271288
// runs a scrub for a blob in a given volume to detect errors
272289
// https://en.wikipedia.org/wiki/Data_scrubbing
273290
// we could actually just do a Fetch() but that would require access to the encryption keys.

pkg/stoserver/stodiskaccess/diskaccess_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,14 @@ func (t *testingBlobStorage) RawStore(_ context.Context, ref stotypes.BlobRef, c
432432
return nil
433433
}
434434

435+
func (t *testingBlobStorage) RawDelete(ctx context.Context, ref stotypes.BlobRef) error {
436+
if _, has := t.files[ref.AsHex()]; !has {
437+
return os.ErrNotExist
438+
}
439+
delete(t.files, ref.AsHex())
440+
return nil
441+
}
442+
435443
func xorSlices(a []byte, b []byte) []byte {
436444
if len(a) != len(b) {
437445
panic("nope")

pkg/stoserver/stoservertypes/commands.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,17 @@
261261
{ "key": "OnlyIfRedundancy", "title": "Abort if blob would lose last replica", "type": "checkbox", "help": "Failsafe for you to reconsider if it losing last replica would come as a surprise." }
262262
]
263263
},
264+
{
265+
"command": "volume.DeleteBlob",
266+
"chain": "public",
267+
"ctor": ["Id"],
268+
"crudNature": "delete",
269+
"title": "Delete blob (or mark corrupted one lost)",
270+
"fields": [
271+
{ "key": "Id", "type": "integer", "hideIfDefaultValue": true },
272+
{ "key": "Ref", "title": "Blob reference" }
273+
]
274+
},
264275
{
265276
"command": "volume.Decommission",
266277
"chain": "public",

0 commit comments

Comments
 (0)