Получение снимка

This commit is contained in:
Alexander Zhirov 2025-09-10 22:21:56 +03:00
parent 8631c65e39
commit 541d09b8f4
Signed by: alexander
GPG key ID: C8D8BE544A27C511
4 changed files with 106 additions and 88 deletions

View file

@ -8,6 +8,12 @@ import std.format : format;
import zstd; import zstd;
import std.exception : enforce;
import std.stdio : writeln;
import std.conv : to;
import std.file : write;
// CAS-хранилище (Content-Addressable Storage) со снапшотами // CAS-хранилище (Content-Addressable Storage) со снапшотами
final class CAS final class CAS
{ {
@ -26,7 +32,6 @@ public:
ubyte[32] hashSource = digest!SHA256(data); ubyte[32] hashSource = digest!SHA256(data);
// Сделать запрос в БД по filePath и сверить хеш файлов // Сделать запрос в БД по filePath и сверить хеш файлов
import std.stdio : writeln;
// writeln(hashSource.length); // writeln(hashSource.length);
@ -86,4 +91,31 @@ public:
// Вернуть ID манифеста // Вернуть ID манифеста
return 0; return 0;
} }
void restoreSnapshot()
{
string restoreFile = "/tmp/restore.d";
foreach (Snapshot snapshot; _db.getSnapshots("/tmp/text")) {
auto dataChunks = _db.getChunks(snapshot.id);
ubyte[] content;
foreach (SnapshotDataChunk chunk; dataChunks) {
ubyte[] bytes;
if (chunk.zstd) {
enforce(chunk.zSize == chunk.content.length, "Размер сжатого фрагмента не соответствует ожидаемому");
bytes = cast(ubyte[]) uncompress(chunk.content);
} else {
bytes = chunk.content;
}
enforce(chunk.size == bytes.length, "Оригинальный размер не соответствует ожидаемому");
content ~= bytes;
}
enforce(snapshot.fileSha256 == digest!SHA256(content), "Хеш-сумма файла не совпадает");
write(snapshot.filePath ~ snapshot.id.to!string, content);
}
}
} }

View file

@ -103,7 +103,7 @@ public:
ON CONFLICT (sha256) DO NOTHING ON CONFLICT (sha256) DO NOTHING
}, },
blob.sha256[], blob.sha256[],
blob.zSize ? blob.zSha256[] : null, blob.zstd ? blob.zSha256[] : null,
blob.size, blob.size,
blob.zSize, blob.zSize,
blob.content, blob.content,
@ -187,97 +187,71 @@ public:
// // --- чтение --- // // --- чтение ---
// Snapshot getSnapshot(long id) Snapshot[] getSnapshots(string filePath)
// { {
// auto queryResult = sql( auto queryResult = sql(
// q{ q{
// SELECT id, file_path, file_sha256, label, created_utc, source_length, SELECT id, file_path, file_sha256, label, created_utc, source_length,
// algo_min, algo_normal, algo_max, mask_s, mask_l, status algo_min, algo_normal, algo_max, mask_s, mask_l, status
// FROM snapshots WHERE id = ? FROM snapshots WHERE file_path = ?
// }, id); }, filePath
);
// Snapshot s; Snapshot[] snapshots;
// bool found = false; // bool found = false;
// foreach (row; queryResult) foreach (row; queryResult)
// { {
// s.id = row[0].to!long; Snapshot snapshot;
// s.file_path = row[1].to!string;
// s.label = row[2].to!string; snapshot.id = row["id"].to!long;
// s.created_utc = row[3].to!string; snapshot.filePath = row["file_path"].to!string;
// s.source_length = row[4].to!long; snapshot.fileSha256 = cast(ubyte[]) row["file_sha256"].dup;
// s.algo_min = row[5].to!long; snapshot.label = row["label"].to!string;
// s.algo_normal = row[6].to!long; snapshot.createdUtc = row["created_utc"].to!string;
// s.algo_max = row[7].to!long; snapshot.sourceLength = row["source_length"].to!long;
// s.mask_s = row[8].to!long; snapshot.algoMin = row["algo_min"].to!long;
// s.mask_l = row[9].to!long; snapshot.algoNormal = row["algo_normal"].to!long;
// s.status = cast(SnapshotStatus) row[10].to!int; snapshot.algoMax = row["algo_max"].to!long;
snapshot.maskS = row["mask_s"].to!long;
snapshot.maskL = row["mask_l"].to!long;
snapshot.status = cast(SnapshotStatus)row["status"].to!int;
// found = true; // found = true;
// break; snapshots ~= snapshot;
// } }
// enforce(found, "getSnapshot: not found"); // enforce(found, "getSnapshot: not found");
// return s; return snapshots;
// } }
// SnapshotChunk[] getSnapshotChunks(long snapshotId) SnapshotDataChunk[] getChunks(long snapshotId) {
// { auto queryResult = sql(
// auto r = sql(q{ q{
// SELECT snapshot_id,chunk_index,COALESCE(offset,0),size,sha256 SELECT sc.chunk_index, sc.offset, sc.size,
// FROM snapshot_chunks b.content, b.zstd, b.z_size, b.sha256, b.z_sha256
// WHERE snapshot_id=? ORDER BY chunk_index FROM snapshot_chunks sc
// }, snapshotId); JOIN blobs b ON b.sha256 = sc.sha256
WHERE sc.snapshot_id = ?
ORDER BY sc.chunk_index
}, snapshotId
);
// auto acc = appender!SnapshotChunk[]; SnapshotDataChunk[] sdchs;
// foreach (row; r)
// {
// SnapshotChunk ch;
// ch.snapshot_id = row[0].to!long;
// ch.chunk_index = row[1].to!long;
// ch.offset = row[2].to!long;
// ch.size = row[3].to!long;
// const(ubyte)[] sha = cast(const(ubyte)[]) row[4]; foreach (row; queryResult)
// enforce(sha.length == 32, "getSnapshotChunks: sha256 blob length != 32"); {
// ch.sha256[] = sha[]; SnapshotDataChunk sdch;
// acc.put(ch); sdch.chunkIndex = row["chunk_index"].to!long;
// } sdch.offset = row["offset"].to!long;
// return acc.data; sdch.size = row["size"].to!long;
// } sdch.content = cast(ubyte[]) row["content"].dup;
sdch.zstd = cast(bool) row["zstd"].to!int;
sdch.zSize = row["z_size"].to!long;
sdch.sha256 = cast(ubyte[]) row["sha256"].dup;
sdch.zSha256 = cast(ubyte[]) row["z_sha256"].dup;
// /// Вариант без `out`: вернуть Nullable sdchs ~= sdch;
// Nullable!Snapshot maybeGetSnapshotByLabel(string label) }
// {
// auto r = sql(q{
// SELECT id,file_path,label,created_utc,source_length,
// algo_min,algo_normal,algo_max,mask_s,mask_l,status
// FROM snapshots
// WHERE label=? ORDER BY id DESC LIMIT 1
// }, label);
// foreach (row; r) return sdchs;
// { }
// Snapshot s;
// s.id = row[0].to!long;
// s.file_path = row[1].to!string;
// s.label = row[2].to!string;
// s.created_utc = row[3].to!string;
// s.source_length = row[4].to!long;
// s.algo_min = row[5].to!long;
// s.algo_normal = row[6].to!long;
// s.algo_max = row[7].to!long;
// s.mask_s = row[8].to!long;
// s.mask_l = row[9].to!long;
// s.status = cast(SnapshotStatus) row[10].to!int;
// return typeof(return)(s); // Nullable!Snapshot(s)
// }
// return typeof(return).init; // null/empty
// }
// /// Или жёсткий вариант: вернуть/кинуть
// Snapshot getSnapshotByLabel(string label)
// {
// auto m = maybeGetSnapshotByLabel(label);
// enforce(!m.isNull, "getSnapshotByLabel: not found");
// return m.get;
// }
} }

View file

@ -43,3 +43,14 @@ struct SnapshotChunk
long size; long size;
ubyte[32] sha256; // BLOB(32) ubyte[32] sha256; // BLOB(32)
} }
struct SnapshotDataChunk {
long chunkIndex;
long offset;
long size;
ubyte[] content;
bool zstd;
long zSize;
ubyte[32] sha256;
ubyte[32] zSha256;
}

View file

@ -7,5 +7,6 @@ import std.file : read;
void main() void main()
{ {
auto cas = new CAS("/tmp/base.db", true); auto cas = new CAS("/tmp/base.db", true);
cas.saveSnapshot("/tmp/text", cast(ubyte[]) read("/tmp/text")); // cas.saveSnapshot("/tmp/text", cast(ubyte[]) read("/tmp/text"));
cas.restoreSnapshot();
} }