forked from dlang/cdcdb
		
	Получение снимка
This commit is contained in:
		
							parent
							
								
									8631c65e39
								
							
						
					
					
						commit
						541d09b8f4
					
				
					 4 changed files with 106 additions and 88 deletions
				
			
		| 
						 | 
					@ -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);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
					 | 
				
			||||||
	// 		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;
 | 
					 | 
				
			||||||
	// 		found = true;
 | 
					 | 
				
			||||||
	// 		break;
 | 
					 | 
				
			||||||
	// 	}
 | 
					 | 
				
			||||||
	// 	enforce(found, "getSnapshot: not found");
 | 
					 | 
				
			||||||
	// 	return s;
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// SnapshotChunk[] getSnapshotChunks(long snapshotId)
 | 
								snapshot.id = row["id"].to!long;
 | 
				
			||||||
	// {
 | 
								snapshot.filePath = row["file_path"].to!string;
 | 
				
			||||||
	// 	auto r = sql(q{
 | 
								snapshot.fileSha256 = cast(ubyte[]) row["file_sha256"].dup;
 | 
				
			||||||
	// 		SELECT snapshot_id,chunk_index,COALESCE(offset,0),size,sha256
 | 
								snapshot.label = row["label"].to!string;
 | 
				
			||||||
	// 		FROM snapshot_chunks
 | 
								snapshot.createdUtc = row["created_utc"].to!string;
 | 
				
			||||||
	// 		WHERE snapshot_id=? ORDER BY chunk_index
 | 
								snapshot.sourceLength = row["source_length"].to!long;
 | 
				
			||||||
	// 	}, snapshotId);
 | 
								snapshot.algoMin = row["algo_min"].to!long;
 | 
				
			||||||
 | 
								snapshot.algoNormal = row["algo_normal"].to!long;
 | 
				
			||||||
 | 
								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;
 | 
				
			||||||
 | 
								snapshots ~= snapshot;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// enforce(found, "getSnapshot: not found");
 | 
				
			||||||
 | 
							return snapshots;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 	auto acc = appender!SnapshotChunk[];
 | 
						SnapshotDataChunk[] getChunks(long snapshotId) {
 | 
				
			||||||
	// 	foreach (row; r)
 | 
							auto queryResult = sql(
 | 
				
			||||||
	// 	{
 | 
								q{
 | 
				
			||||||
	// 		SnapshotChunk ch;
 | 
									SELECT sc.chunk_index, sc.offset, sc.size,
 | 
				
			||||||
	// 		ch.snapshot_id = row[0].to!long;
 | 
										b.content, b.zstd, b.z_size, b.sha256, b.z_sha256
 | 
				
			||||||
	// 		ch.chunk_index = row[1].to!long;
 | 
									FROM snapshot_chunks sc
 | 
				
			||||||
	// 		ch.offset = row[2].to!long;
 | 
									JOIN blobs b ON b.sha256 = sc.sha256
 | 
				
			||||||
	// 		ch.size = row[3].to!long;
 | 
									WHERE sc.snapshot_id = ?
 | 
				
			||||||
 | 
									ORDER BY sc.chunk_index
 | 
				
			||||||
 | 
								}, snapshotId
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 		const(ubyte)[] sha = cast(const(ubyte)[]) row[4];
 | 
							SnapshotDataChunk[] sdchs;
 | 
				
			||||||
	// 		enforce(sha.length == 32, "getSnapshotChunks: sha256 blob length != 32");
 | 
					 | 
				
			||||||
	// 		ch.sha256[] = sha[];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 		acc.put(ch);
 | 
							foreach (row; queryResult)
 | 
				
			||||||
	// 	}
 | 
							{
 | 
				
			||||||
	// 	return acc.data;
 | 
								SnapshotDataChunk sdch;
 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// /// Вариант без `out`: вернуть Nullable
 | 
								sdch.chunkIndex = row["chunk_index"].to!long;
 | 
				
			||||||
	// Nullable!Snapshot maybeGetSnapshotByLabel(string label)
 | 
								sdch.offset = row["offset"].to!long;
 | 
				
			||||||
	// {
 | 
								sdch.size = row["size"].to!long;
 | 
				
			||||||
	// 	auto r = sql(q{
 | 
								sdch.content = cast(ubyte[]) row["content"].dup;
 | 
				
			||||||
	// 		SELECT id,file_path,label,created_utc,source_length,
 | 
								sdch.zstd = cast(bool) row["zstd"].to!int;
 | 
				
			||||||
	// 			algo_min,algo_normal,algo_max,mask_s,mask_l,status
 | 
								sdch.zSize = row["z_size"].to!long;
 | 
				
			||||||
	// 		FROM snapshots
 | 
								sdch.sha256 = cast(ubyte[]) row["sha256"].dup;
 | 
				
			||||||
	// 		WHERE label=? ORDER BY id DESC LIMIT 1
 | 
								sdch.zSha256 = cast(ubyte[]) row["z_sha256"].dup;
 | 
				
			||||||
	// 	}, label);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 	foreach (row; r)
 | 
								sdchs ~= sdch;
 | 
				
			||||||
	// 	{
 | 
							}
 | 
				
			||||||
	// 		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
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// /// Или жёсткий вариант: вернуть/кинуть
 | 
							return sdchs;
 | 
				
			||||||
	// Snapshot getSnapshotByLabel(string label)
 | 
						}
 | 
				
			||||||
	// {
 | 
					 | 
				
			||||||
	// 	auto m = maybeGetSnapshotByLabel(label);
 | 
					 | 
				
			||||||
	// 	enforce(!m.isNull, "getSnapshotByLabel: not found");
 | 
					 | 
				
			||||||
	// 	return m.get;
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue