init
This commit is contained in:
commit
dc0c8349c7
18 changed files with 666 additions and 0 deletions
36
source/cdcdb/cdc/cas.d
Normal file
36
source/cdcdb/cdc/cas.d
Normal file
|
@ -0,0 +1,36 @@
|
|||
module cdcdb.cdc.cas;
|
||||
|
||||
import cdcdb.db;
|
||||
import cdcdb.cdc.core;
|
||||
|
||||
final class CAS
|
||||
{
|
||||
private:
|
||||
DBLite _db;
|
||||
public:
|
||||
this(string database)
|
||||
{
|
||||
_db = new DBLite(database);
|
||||
}
|
||||
|
||||
size_t saveSnapshot(const(ubyte)[] data)
|
||||
{
|
||||
// Параметры для CDC вынести в отдельные настройки (продумать)
|
||||
auto cdc = new CDC(100, 200, 500, 0xFF, 0x0F);
|
||||
// Разбить на фрагменты
|
||||
auto chunks = cdc.split(data);
|
||||
|
||||
import std.stdio : writeln;
|
||||
|
||||
_db.beginImmediate();
|
||||
// Записать фрагменты в БД
|
||||
foreach (chunk; chunks)
|
||||
{
|
||||
writeln(chunk.index);
|
||||
}
|
||||
_db.commit();
|
||||
// Записать манифест в БД
|
||||
// Вернуть ID манифеста
|
||||
return 0;
|
||||
}
|
||||
}
|
90
source/cdcdb/cdc/core.d
Normal file
90
source/cdcdb/cdc/core.d
Normal file
|
@ -0,0 +1,90 @@
|
|||
module cdcdb.cdc.core;
|
||||
|
||||
import cdcdb.cdc.types;
|
||||
|
||||
import std.digest.sha : SHA256, digest;
|
||||
|
||||
final class CDC
|
||||
{
|
||||
private:
|
||||
size_t _minSize, _normalSize, _maxSize;
|
||||
ulong _maskS, _maskL;
|
||||
// _gear
|
||||
mixin(import("gear.d"));
|
||||
|
||||
size_t cut(const(ubyte)[] src) @safe nothrow
|
||||
{
|
||||
size_t size = src.length;
|
||||
if (size == 0)
|
||||
return 0;
|
||||
if (size <= _minSize)
|
||||
return size;
|
||||
|
||||
if (size > _maxSize)
|
||||
size = _maxSize;
|
||||
auto normalSize = _normalSize;
|
||||
if (size < normalSize)
|
||||
normalSize = size;
|
||||
|
||||
ulong fingerprint = 0;
|
||||
size_t index;
|
||||
|
||||
// инициализация без cut-check
|
||||
while (index < _minSize)
|
||||
{
|
||||
fingerprint = (fingerprint << 1) + _gear[src[index]];
|
||||
++index;
|
||||
}
|
||||
// строгая маска
|
||||
while (index < normalSize)
|
||||
{
|
||||
fingerprint = (fingerprint << 1) + _gear[src[index]];
|
||||
if ((fingerprint & _maskS) == 0)
|
||||
return index;
|
||||
++index;
|
||||
}
|
||||
// слабая маска
|
||||
while (index < size)
|
||||
{
|
||||
fingerprint = (fingerprint << 1) + _gear[src[index]];
|
||||
if ((fingerprint & _maskL) == 0)
|
||||
return index;
|
||||
++index;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
public:
|
||||
this(size_t minSize, size_t normalSize, size_t maxSize, ulong maskS, ulong maskL) @safe @nogc nothrow
|
||||
{
|
||||
assert(minSize > 0 && minSize < normalSize && normalSize < maxSize,
|
||||
"Неверные размеры: требуется min < normal < max и min > 0");
|
||||
_minSize = minSize;
|
||||
_normalSize = normalSize;
|
||||
_maxSize = maxSize;
|
||||
_maskS = maskS;
|
||||
_maskL = maskL;
|
||||
}
|
||||
|
||||
Chunk[] split(const(ubyte)[] data) @safe
|
||||
{
|
||||
Chunk[] chunks;
|
||||
if (data.length == 0)
|
||||
return chunks;
|
||||
chunks.reserve(data.length / _normalSize);
|
||||
size_t offset = 0;
|
||||
size_t index = 1;
|
||||
|
||||
while (offset < data.length)
|
||||
{
|
||||
auto size = cut(data[offset .. $]);
|
||||
auto bytes = data[offset .. offset + size];
|
||||
ubyte[32] hash = digest!SHA256(bytes);
|
||||
chunks ~= Chunk(index, offset, size, hash);
|
||||
|
||||
offset += size;
|
||||
++index;
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
}
|
66
source/cdcdb/cdc/gear.d
Normal file
66
source/cdcdb/cdc/gear.d
Normal file
|
@ -0,0 +1,66 @@
|
|||
immutable ulong[256] _gear = [
|
||||
0x2722039f43c57a70, 0x338c1bd5b7ac5204, 0xf9f2c73ff33c98c0, 0x7dee12e6cd31cb32,
|
||||
0x9688335e0f2decfd, 0x5307003c8e60b963, 0xfd2a2848eb358095, 0xc3614773074ee6b7,
|
||||
0x6e35235234b6ed0a, 0x9d4cfa9d8e3850cc, 0xaa1b3d8af8ad86bd, 0x79c6d2e28bfb333d,
|
||||
0x3df08966a00c33ec, 0xfd58bbf83f38c690, 0x5ef9ee9a4552545b, 0x099192a7e5599bdc,
|
||||
0xa8f2419947f21017, 0xd6a03d010f2fda7c, 0x1fe53de04074fc20, 0x75b5aff7c66605f8,
|
||||
0x1a94b7484bf509a9, 0xbf2e371a53466ded, 0xedcf13f8eb0f0fdf, 0xfba81285ead7dafe,
|
||||
0x839fb29274557fa5, 0xeefe64b15cc7f7f0, 0x7d15f8e862726515, 0x59b416e43cca2adc,
|
||||
0x9c2c925dcde12d4a, 0xf3df9373c3e63a07, 0x747cb5ec08ffa4ef, 0x26c93138f3f19b29,
|
||||
0xcdade11723bd59ed, 0xc7a6a7d0c18642cb, 0x88c2976f22f5d084, 0x7c48f54cdaf480fe,
|
||||
0x91ea7c7fd3d06d54, 0xed3e31236e8c9366, 0xa16da2234f420bc4, 0x5ee6136449d30974,
|
||||
0xe32a921921181e16, 0xa6ab2fb8c7212257, 0x754a8a581ce819ca, 0x22b2de3e7c7a2f57,
|
||||
0xd2773285e49b9160, 0x19b0449384554129, 0x145e7d2c46da460e, 0xdd720d0e79a3615d,
|
||||
0x621ae4f0ad576223, 0x4f6da235cc25d5c9, 0x6d6d39e005d67437, 0x5839c8f3d2f71122,
|
||||
0x691a668bc7f5c153, 0xb2eb484f8546c61d, 0x7955c346c34cbcdc, 0x413c2a0ba6fd0a88,
|
||||
0x29ad3d418323592b, 0xb7d04d0abf3f8d50, 0x76e742f91dfc77ea, 0x8a5c80d1a0d5ff5c,
|
||||
0xce1aa9b0bdd16adc, 0x74e4bd6f412c8186, 0xbf1dddc8f63dfc08, 0x11dcb84c1b5c32cb,
|
||||
0x3320ed259fc0d8c0, 0x13dbd4c934c58e01, 0x344b61dd3741a9f9, 0x935861bea84a6f81,
|
||||
0xaf70eea3844052f9, 0xc0a83f93799c2e81, 0xdd23b2a943a5af16, 0x05b4efd89b3e818b,
|
||||
0x75b2a3d0fe099aec, 0x5aab5599ae580c37, 0xe9b64238ed626a6b, 0xb63f47c31c31ec1d,
|
||||
0x0b50ee03c3425dde, 0xf287ebed924249f6, 0xe09eee62604318c4, 0x0d334cb1bd82bc13,
|
||||
0xc41abf3d95b18620, 0x869c3bc45e2c9edf, 0x526de53484e093c7, 0xc640fee4784fd9ce,
|
||||
0x761637787d81c0ea, 0x817bf175cb17e903, 0x94a4846f1158f988, 0x99c254e5f8e698e0,
|
||||
0xa4623d4d1b76352e, 0x326dae4493475c3a, 0xed2944be79511208, 0x163a0a9b65f40339,
|
||||
0x336489f8c2f6190c, 0x670d217f8e6bee33, 0x662e19c285c5a4a1, 0xcab8f4512d0b251a,
|
||||
0x61d4476812ed1017, 0x0ec77209307430af, 0x20a94905901093dc, 0xaa9fe2cae9ffa699,
|
||||
0xc75f757de6c045dc, 0x141ef38478656459, 0x9b3ce9c4e3dd7858, 0x3ab62b9aa45a3d0d,
|
||||
0x61a89423e18e5e68, 0x3802972ecadf592d, 0xcfc85c5724ff3af8, 0x381ee916e97a628a,
|
||||
0x2fa2c37a040e488a, 0x9813a505b4ca4036, 0xc4254f1aaf7b2f42, 0xe8a0720b79a1188d,
|
||||
0xe663a71adb5d53e3, 0x6e3b5927934102af, 0xbd8c502741b1fcb1, 0x1af6fa2fb1d2e5a6,
|
||||
0xc88d367a79d06f5d, 0x29fe7cdab66530d9, 0x34bef2ebe612d95f, 0x9ab6977a57db1fa2,
|
||||
0x73774fc29deac09a, 0x7832f37495fd28fb, 0x1559a3badfbd42a6, 0x7e6831522a50d2bc,
|
||||
0xddb8564f3aafe3b7, 0x86acb9eca71bc09d, 0x21b0a9469727d4fc, 0x26d3b66f525ebcab,
|
||||
0x77e3fd126fd97e3a, 0x5306b81a9fe2a92e, 0x7292138f116d8911, 0x285b466c9939b076,
|
||||
0x40527805d9a4379d, 0x8986c05119c7ca1e, 0x6a7890c402303c31, 0xb1b109dc109405bc,
|
||||
0x1d71f3997b288f30, 0xfa203ff4dc9ea72c, 0x8ae3eea975cc92da, 0x3468e4305eabb928,
|
||||
0xd79c37e720467df1, 0x011e6490c0f832d2, 0x29ce2ada8509647a, 0xb4e325b9f3ba783c,
|
||||
0xa812ca4fad720763, 0x0cdf098645ccb476, 0xf6b47e21637fcd76, 0x3597f48148a297de,
|
||||
0x5875212868ab81ec, 0x1ea36243940596bb, 0xfd93ac7c39a27586, 0xabb09b0f803a7214,
|
||||
0x8cc8ec1ea21a16af, 0x824a0db50ae906d1, 0x3d972fb701ca3e70, 0xda60d493e9a20bd0,
|
||||
0x97d282f6bda26087, 0x9bc8f7842af296d0, 0x14804a1663a0cf7e, 0x3b71cc25885e75f3,
|
||||
0x131adc05e336042b, 0x566aa36d26eee86c, 0x97d4c4d4fd4b0dd1, 0xd2407b1485c7bee1,
|
||||
0xcad613e7b92e6df1, 0xe3ceccd99d975088, 0x99e6b93ff96a2636, 0x1ad75dbed057f0d0,
|
||||
0x5e3ba609dd100c6e, 0x9c5efa00b33a18f3, 0xad89369e692bdb28, 0xf7a546fca26d1d7d,
|
||||
0x5813db1fe943575f, 0x24c3467f03a144ae, 0xc892f2ce492cb7c8, 0xc44672263508d34b,
|
||||
0xd400e1c0a5734a40, 0x3ca24ee74bf8e84f, 0xd83bd4e907c351a5, 0xe142297005fa9aa8,
|
||||
0x0f6d796cf68abda0, 0x6c8e25bc6d9ae2e8, 0xccc235f322a42cf3, 0xabaf39cea8ca450c,
|
||||
0x02b9cdf615a0d7b6, 0x8aaf7d8b55d4dc39, 0xbe2c2bc6ef13c6c5, 0x6ad98aa4a4bc610f,
|
||||
0x1051a62ac2a2b434, 0xbd167e6eba260d35, 0xb9b86ac04ac4f811, 0xabe8a6453e196739,
|
||||
0x439ff734b19246b4, 0xcea324040c9e8981, 0x87f55cf1035e1a22, 0xa227d679c33597f9,
|
||||
0xbf4d654b6cdd0015, 0xc0302ec55f87a46e, 0xed32173466c70a83, 0x8ceb757b648d2bf2,
|
||||
0x1873757a6d17446b, 0xeb0f366fea62e77e, 0x145aa2795d34dd93, 0x2fc378be4c471db0,
|
||||
0x6d1274fb8f6364a2, 0x602a56fd1cc36728, 0x5f8aa6e0c892b4b5, 0x33e2c5653d8b1ad6,
|
||||
0x1f6c8b2a004714f4, 0x4042b98d54acbfef, 0x4606386f11f6456f, 0xf56bd21a8a35c540,
|
||||
0xd2b23c57b3718e1f, 0x94726832fe96e61d, 0xa225b072752a823b, 0x0bd957cf585f8cda,
|
||||
0x533d819bb30b4221, 0xda0f9cff9a0115fa, 0xd14de3b8fe3354ea, 0xa96328e96d9364c0,
|
||||
0x9078dc0eff2676ab, 0x22585cd4521c6210, 0x5903254df4e402a5, 0x1b54b71b55ae697a,
|
||||
0xb899b86756b2aa39, 0x5d5d2dd5cd0bce8b, 0x7b3a78a4a0662015, 0xa9fbfc7678fc7931,
|
||||
0xa732d694f6ab64a0, 0x9fc960e7db3e9716, 0x76c765948f3c2ba5, 0x076a509dca2a4349,
|
||||
0xca5bfc5973661e59, 0x454ec4d49bddd45d, 0x56115e001997cee2, 0xd689eb8926051c7f,
|
||||
0xf50df8ca9c355e3f, 0x88a375a9f0492a69, 0xe059fd001d50439a, 0x765c5d6f66d5e788,
|
||||
0xaf57f4eea178f896, 0x06e8cca68730fbbd, 0xb7b1f6f86904ce4e, 0x3c3b10b0c08cf0bf,
|
||||
0x1e0e310524778bd4, 0xd65d7cd93cde7c69, 0x18543b187c77fcf3, 0x180f6cdd1af3a60a,
|
||||
0xe1cd4c2bc3656704, 0x218fdfc5aa282d00, 0x844eeaf2e439b242, 0x05df1a59e415b4c6,
|
||||
0x14abdd3ace097c2c, 0x7f3b0705b04b14d4, 0xf69c57f60180332b, 0x165fc3f0e65db80f
|
||||
];
|
3
source/cdcdb/cdc/package.d
Normal file
3
source/cdcdb/cdc/package.d
Normal file
|
@ -0,0 +1,3 @@
|
|||
module cdcdb.cdc;
|
||||
|
||||
public import cdcdb.cdc.cas;
|
20
source/cdcdb/cdc/types.d
Normal file
20
source/cdcdb/cdc/types.d
Normal file
|
@ -0,0 +1,20 @@
|
|||
module cdcdb.cdc.types;
|
||||
|
||||
/// Единица разбиения
|
||||
struct Chunk
|
||||
{
|
||||
size_t index; // 1..N
|
||||
size_t offset; // смещение в исходном буфере
|
||||
size_t size; // размер чанка
|
||||
ubyte[32] sha256; // hex(SHA-256) содержимого
|
||||
}
|
||||
|
||||
/// Метаданные снимка
|
||||
struct SnapshotInfo
|
||||
{
|
||||
size_t id;
|
||||
string createdUTC; // ISO-8601
|
||||
string label;
|
||||
size_t sourceLength;
|
||||
size_t chunks;
|
||||
}
|
47
source/cdcdb/db/dblite.d
Normal file
47
source/cdcdb/db/dblite.d
Normal file
|
@ -0,0 +1,47 @@
|
|||
module cdcdb.db.dblite;
|
||||
|
||||
import arsd.sqlite;
|
||||
import std.file : exists, isFile;
|
||||
|
||||
final class DBLite : Sqlite
|
||||
{
|
||||
private:
|
||||
string _dbPath;
|
||||
// _scheme
|
||||
mixin(import("scheme.d"));
|
||||
public:
|
||||
this(string database)
|
||||
{
|
||||
_dbPath = database;
|
||||
super(database);
|
||||
|
||||
foreach (schemeQuery; _scheme)
|
||||
{
|
||||
sql(schemeQuery);
|
||||
}
|
||||
|
||||
query("PRAGMA journal_mode=WAL");
|
||||
query("PRAGMA synchronous=NORMAL");
|
||||
query("PRAGMA foreign_keys=ON");
|
||||
}
|
||||
|
||||
void beginImmediate()
|
||||
{
|
||||
query("BEGIN IMMEDIATE");
|
||||
}
|
||||
|
||||
void commit()
|
||||
{
|
||||
query("COMMIT");
|
||||
}
|
||||
|
||||
void rollback()
|
||||
{
|
||||
query("ROLLBACK");
|
||||
}
|
||||
|
||||
SqliteResult sql(T...)(string queryText, T args)
|
||||
{
|
||||
return cast(SqliteResult) query(queryText, args);
|
||||
}
|
||||
}
|
3
source/cdcdb/db/package.d
Normal file
3
source/cdcdb/db/package.d
Normal file
|
@ -0,0 +1,3 @@
|
|||
module cdcdb.db;
|
||||
|
||||
public import cdcdb.db.dblite;
|
75
source/cdcdb/db/scheme.d
Normal file
75
source/cdcdb/db/scheme.d
Normal file
|
@ -0,0 +1,75 @@
|
|||
auto _scheme = [
|
||||
q{
|
||||
-- Метаданные снапшота
|
||||
CREATE TABLE IF NOT EXISTS snapshots (
|
||||
-- Уникальный числовой идентификатор снимка. Используется во внешних ключах.
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
-- Произвольная метка/название снимка.
|
||||
label TEXT,
|
||||
-- Время создания записи в UTC. По умолчанию - сейчас.
|
||||
created_utc TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP),
|
||||
-- Полная длина исходного файла в байтах для этого снимка (до разбиения на чанки).
|
||||
source_length INTEGER NOT NULL,
|
||||
-- Пороговые размеры FastCDC (минимальный/целевой/максимальный размер чанка) в байтах.
|
||||
-- Фиксируются здесь, чтобы позже можно было корректно пересобрать/сравнить.
|
||||
algo_min INTEGER NOT NULL,
|
||||
algo_normal INTEGER NOT NULL,
|
||||
algo_max INTEGER NOT NULL,
|
||||
-- Маски для определения границ чанков (быстрый роллинг-хэш/FastCDC).
|
||||
-- Обычно степени вида 2^n - 1. Хранятся для воспроизводимости.
|
||||
mask_s INTEGER NOT NULL,
|
||||
mask_l INTEGER NOT NULL,
|
||||
-- Состояние снимка:
|
||||
-- pending - метаданные созданы, состав не полностью загружен;
|
||||
-- ready - все чанки привязаны, снимок готов к использованию.
|
||||
status TEXT NOT NULL DEFAULT "pending" CHECK (status IN ("pending","ready"))
|
||||
)
|
||||
},
|
||||
q{
|
||||
-- Уникальные куски содержимого (сам контент в БД)
|
||||
CREATE TABLE IF NOT EXISTS blobs (
|
||||
-- Хэш содержимого чанка. Ключ обеспечивает дедупликацию: одинаковый контент хранится один раз.
|
||||
sha256 TEXT PRIMARY KEY,
|
||||
-- Размер этого чанка в байтах.
|
||||
size INTEGER NOT NULL,
|
||||
-- Сырые байты чанка.
|
||||
content BLOB NOT NULL,
|
||||
-- Когда этот контент впервые появился в базе (UTC).
|
||||
created_utc TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP)
|
||||
)
|
||||
},
|
||||
q{
|
||||
-- Состав снапшота (порядок чанков важен)
|
||||
CREATE TABLE IF NOT EXISTS snapshot_chunks (
|
||||
-- Ссылка на snapshots.id. Определяет, к какому снимку относится строка.
|
||||
snapshot_id INTEGER NOT NULL,
|
||||
-- Позиция чанка в снимке (индексация).
|
||||
-- Обеспечивает порядок сборки.
|
||||
chunk_index INTEGER NOT NULL,
|
||||
-- Смещение чанка в исходном файле в байтах.
|
||||
-- Можно восстановить как сумму size предыдущих чанков по chunk_index,
|
||||
-- но хранение ускоряет проверки/отладку.
|
||||
offset INTEGER,
|
||||
-- Размер именно этого чанка в байтах (дублирует blobs.size для быстрого доступа и валидации).
|
||||
size INTEGER NOT NULL,
|
||||
-- Ссылка на blobs.sha256. Привязывает позицию в снимке к конкретному содержимому.
|
||||
sha256 TEXT NOT NULL,
|
||||
-- Гарантирует уникальность позиции чанка в рамках снимка и задаёт естественный порядок.
|
||||
PRIMARY KEY (snapshot_id, chunk_index),
|
||||
-- При удалении снимка его строки состава удаляются автоматически.
|
||||
-- Обновления id каскадятся (на практике id не меняют).
|
||||
FOREIGN KEY (snapshot_id) REFERENCES snapshots(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
-- Нельзя удалить blob, если он где-то используется (RESTRICT).
|
||||
-- Обновление хэша каскадится (редкий случай).
|
||||
FOREIGN KEY (sha256) REFERENCES blobs(sha256) ON UPDATE CASCADE ON DELETE RESTRICT
|
||||
)
|
||||
},
|
||||
q{
|
||||
-- Быстрый выбор всех чанков конкретного снимка (частый запрос).
|
||||
CREATE INDEX IF NOT EXISTS idx_snapshot_chunks_snapshot ON snapshot_chunks(snapshot_id)
|
||||
},
|
||||
q{
|
||||
-- Быстрый обратный поиск: где используется данный blob (для GC/аналитики).
|
||||
CREATE INDEX IF NOT EXISTS idx_snapshot_chunks_sha ON snapshot_chunks(sha256)
|
||||
}
|
||||
];
|
151
source/cdcdb/db/scheme.md
Normal file
151
source/cdcdb/db/scheme.md
Normal file
|
@ -0,0 +1,151 @@
|
|||
# Схемы базы данных для хранения снимков (фрагментов)
|
||||
|
||||
## Структура базы данных
|
||||
```mermaid
|
||||
erDiagram
|
||||
%% Композитный PK у SNAPSHOT_CHUNKS: (snapshot_id, chunk_index)
|
||||
|
||||
SNAPSHOTS {
|
||||
int id PK
|
||||
string label
|
||||
string created_utc
|
||||
int source_length
|
||||
int algo_min
|
||||
int algo_normal
|
||||
int algo_max
|
||||
int mask_s
|
||||
int mask_l
|
||||
string status
|
||||
}
|
||||
|
||||
BLOBS {
|
||||
string sha256 PK
|
||||
int size
|
||||
blob content
|
||||
string created_utc
|
||||
}
|
||||
|
||||
SNAPSHOT_CHUNKS {
|
||||
int snapshot_id FK
|
||||
int chunk_index
|
||||
int offset
|
||||
int size
|
||||
string sha256 FK
|
||||
}
|
||||
|
||||
%% Связи и поведение внешних ключей
|
||||
SNAPSHOTS ||--o{ SNAPSHOT_CHUNKS : "1:N, ON DELETE CASCADE"
|
||||
BLOBS ||--o{ SNAPSHOT_CHUNKS : "1:N, ON DELETE RESTRICT"
|
||||
```
|
||||
|
||||
## Схема последовательности записи в базу данных
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant APP as Приложение
|
||||
participant CH as Разбиение на чанки (FastCDC)
|
||||
participant HS as Хеширование (SHA-256)
|
||||
participant DB as База данных (SQLite)
|
||||
|
||||
Note over APP,DB: Подготовка
|
||||
APP->>DB: Открывает соединение, включает PRAGMA (WAL, foreign_keys=ON)
|
||||
APP->>DB: BEGIN IMMEDIATE (начать транзакцию с блокировкой на запись)
|
||||
|
||||
Note over APP,DB: Создание метаданных снимка
|
||||
APP->>DB: INSERT INTO snapshots(label, source_length, algo_min, algo_normal, algo_max, mask_s, mask_l, status='pending')
|
||||
DB-->>APP: id снимка = last_insert_rowid()
|
||||
|
||||
Note over APP,CH: Поток файла → чанки
|
||||
APP->>CH: Читает файл, передает параметры FastCDC (min/normal/max, mask_s/mask_l)
|
||||
loop Для каждого чанка в порядке следования
|
||||
CH-->>APP: Возвращает {chunk_index, offset, size, bytes}
|
||||
|
||||
Note over APP,HS: Хеш содержимого
|
||||
APP->>HS: Вычисляет SHA-256(bytes)
|
||||
HS-->>APP: digest (sha256)
|
||||
|
||||
Note over APP,DB: Дедупликация контента
|
||||
APP->>DB: SELECT 1 FROM blobs WHERE sha256 = ?
|
||||
alt Блоб отсутствует
|
||||
APP->>DB: INSERT INTO blobs(sha256, size, content)
|
||||
DB-->>APP: OK
|
||||
else Блоб уже есть
|
||||
DB-->>APP: Найден (пропускаем вставку содержимого)
|
||||
end
|
||||
|
||||
Note over APP,DB: Привязка чанка к снимку
|
||||
APP->>DB: INSERT INTO snapshot_chunks(snapshot_id, chunk_index, offset, size, sha256)
|
||||
DB-->>APP: OK (PK: (snapshot_id, chunk_index))
|
||||
end
|
||||
|
||||
Note over APP,DB: Валидация и завершение
|
||||
APP->>DB: SELECT SUM(size) FROM snapshot_chunks WHERE snapshot_id = ?
|
||||
DB-->>APP: total_size
|
||||
alt total_size == snapshots.source_length
|
||||
APP->>DB: UPDATE snapshots SET status='ready' WHERE id = ?
|
||||
APP->>DB: COMMIT
|
||||
DB-->>APP: Транзакция зафиксирована
|
||||
else Несоответствие размеров или ошибка
|
||||
APP->>DB: ROLLBACK
|
||||
DB-->>APP: Откат изменений
|
||||
APP-->>APP: Логирует ошибку, возвращает код/исключение
|
||||
end
|
||||
```
|
||||
|
||||
## Схема последовательности восстановления из базы данных
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant APP as Приложение
|
||||
participant DB as База данных (SQLite)
|
||||
participant FS as Целевой файл
|
||||
participant HS as Хеширование (опц.)
|
||||
|
||||
Note over APP,DB: Подготовка к чтению
|
||||
APP->>DB: Открывает соединение (read), BEGIN (снимок чтения)
|
||||
|
||||
Note over APP,DB: Выбор снимка
|
||||
APP->>DB: Находит нужный снимок по id/label, читает status и source_length
|
||||
DB-->>APP: id, status, source_length
|
||||
alt status == "ready"
|
||||
else снимок не готов
|
||||
APP-->>APP: Прерывает восстановление с ошибкой
|
||||
DB-->>APP: END
|
||||
end
|
||||
|
||||
Note over APP,DB: Получение состава снимка
|
||||
APP->>DB: SELECT chunk_index, offset, size, sha256 FROM snapshot_chunks WHERE snapshot_id=? ORDER BY chunk_index
|
||||
DB-->>APP: Строки чанков в порядке chunk_index
|
||||
|
||||
loop Для каждого чанка
|
||||
APP->>DB: SELECT content, size FROM blobs WHERE sha256=?
|
||||
DB-->>APP: content, blob_size
|
||||
|
||||
Note over APP,HS: (опц.) контроль целостности чанка
|
||||
APP->>HS: Вычисляет SHA-256(content)
|
||||
HS-->>APP: digest
|
||||
APP-->>APP: Сверяет digest с sha256 и size с blob_size
|
||||
|
||||
alt offset задан
|
||||
APP->>FS: Позиционируется на offset и пишет content (pwrite/seek+write)
|
||||
else offset отсутствует
|
||||
APP->>FS: Дописывает content в конец файла
|
||||
end
|
||||
end
|
||||
|
||||
Note over APP,DB: Финальная проверка
|
||||
APP-->>APP: Суммирует размеры записанных чанков → total_size
|
||||
APP->>DB: Берёт snapshots.source_length
|
||||
DB-->>APP: source_length
|
||||
alt total_size == source_length
|
||||
APP->>FS: fsync и close
|
||||
DB-->>APP: END
|
||||
APP-->>APP: Успешное восстановление
|
||||
else размеры не совпали
|
||||
APP->>FS: Удаляет/помечает файл как повреждённый
|
||||
DB-->>APP: END
|
||||
APP-->>APP: Фиксирует ошибку (несоответствие сумм)
|
||||
end
|
||||
```
|
3
source/cdcdb/package.d
Normal file
3
source/cdcdb/package.d
Normal file
|
@ -0,0 +1,3 @@
|
|||
module cdcdb;
|
||||
|
||||
public import cdcdb.cdc;
|
Loading…
Add table
Add a link
Reference in a new issue