init
This commit is contained in:
		
						commit
						dc0c8349c7
					
				
					 18 changed files with 666 additions and 0 deletions
				
			
		
							
								
								
									
										18
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					.dub
 | 
				
			||||||
 | 
					docs.json
 | 
				
			||||||
 | 
					__dummy.html
 | 
				
			||||||
 | 
					docs/
 | 
				
			||||||
 | 
					/cdcdb
 | 
				
			||||||
 | 
					cdcdb.so
 | 
				
			||||||
 | 
					cdcdb.dylib
 | 
				
			||||||
 | 
					cdcdb.dll
 | 
				
			||||||
 | 
					cdcdb.a
 | 
				
			||||||
 | 
					cdcdb.lib
 | 
				
			||||||
 | 
					cdcdb-test-*
 | 
				
			||||||
 | 
					*.exe
 | 
				
			||||||
 | 
					*.pdb
 | 
				
			||||||
 | 
					*.o
 | 
				
			||||||
 | 
					*.obj
 | 
				
			||||||
 | 
					*.lst
 | 
				
			||||||
 | 
					bin
 | 
				
			||||||
 | 
					lib
 | 
				
			||||||
							
								
								
									
										37
									
								
								.vscode/launch.json
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								.vscode/launch.json
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,37 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Используйте IntelliSense, чтобы узнать о возможных атрибутах.
 | 
				
			||||||
 | 
						// Наведите указатель мыши, чтобы просмотреть описания существующих атрибутов.
 | 
				
			||||||
 | 
						// Для получения дополнительной информации посетите: https://go.microsoft.com/fwlink/?linkid=830387
 | 
				
			||||||
 | 
						"version": "0.2.0",
 | 
				
			||||||
 | 
						"configurations": [
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"type": "code-d",
 | 
				
			||||||
 | 
								"request": "launch",
 | 
				
			||||||
 | 
								"dubBuild": true,
 | 
				
			||||||
 | 
								"name": "Build & Debug DUB project",
 | 
				
			||||||
 | 
								"cwd": "${command:dubWorkingDirectory}",
 | 
				
			||||||
 | 
								"program": "bin/${command:dubTarget}",
 | 
				
			||||||
 | 
								"args": []
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "Debug D Program with sudo-gdb",
 | 
				
			||||||
 | 
								"type": "cppdbg",
 | 
				
			||||||
 | 
								"request": "launch",
 | 
				
			||||||
 | 
								"program": "${workspaceFolder}/bin/dwatch",
 | 
				
			||||||
 | 
								"args": ["-d", "/tmp/scripts"], // Аргументы командной строки для программы, если нужны
 | 
				
			||||||
 | 
								"stopAtEntry": false, // Остановить на входе в main
 | 
				
			||||||
 | 
								"cwd": "${workspaceFolder}",
 | 
				
			||||||
 | 
								"environment": [],
 | 
				
			||||||
 | 
								"externalConsole": false,
 | 
				
			||||||
 | 
								"MIMode": "gdb",
 | 
				
			||||||
 | 
								"miDebuggerPath": "/usr/bin/sudo-gdb", // Путь к вашему скрипту
 | 
				
			||||||
 | 
								"setupCommands": [
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										"description": "Enable pretty-printing for gdb",
 | 
				
			||||||
 | 
										"text": "-enable-pretty-printing",
 | 
				
			||||||
 | 
										"ignoreFailures": true
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										5
									
								
								.vscode/settings.json
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.vscode/settings.json
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						"editor.insertSpaces": false,
 | 
				
			||||||
 | 
						"editor.tabSize": 4,
 | 
				
			||||||
 | 
						"editor.detectIndentation": false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										14
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					# cdcdb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Подход с использованием CDC (Capture Data Change) для хранения блоков данных в базе данных SQLite.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Сборка
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					# Статическая библиотека
 | 
				
			||||||
 | 
					dub build -c static
 | 
				
			||||||
 | 
					# Динамическая библиотека
 | 
				
			||||||
 | 
					dub build -c dynamic
 | 
				
			||||||
 | 
					# Тест-утилита
 | 
				
			||||||
 | 
					dub build -c binary
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										38
									
								
								dub.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								dub.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,38 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						"authors": [
 | 
				
			||||||
 | 
							"Alexander Zhirov"
 | 
				
			||||||
 | 
						],
 | 
				
			||||||
 | 
						"copyright": "Copyright © 2025, Alexander Zhirov",
 | 
				
			||||||
 | 
						"description": "A CDC Approach for Storing Chunks in an SQLite Database.",
 | 
				
			||||||
 | 
						"license": "BSL-1.0",
 | 
				
			||||||
 | 
						"name": "cdcdb",
 | 
				
			||||||
 | 
						"dependencies": {
 | 
				
			||||||
 | 
							"arsd-official:sqlite": "~>12.0.0",
 | 
				
			||||||
 | 
							"zstd": "~>0.2.1"
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						"stringImportPaths": [
 | 
				
			||||||
 | 
							"source/cdcdb/db",
 | 
				
			||||||
 | 
							"source/cdcdb/cdc"
 | 
				
			||||||
 | 
						],
 | 
				
			||||||
 | 
						"configurations": [
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "static",
 | 
				
			||||||
 | 
								"targetType": "staticLibrary",
 | 
				
			||||||
 | 
								"targetPath": "lib",
 | 
				
			||||||
 | 
								"sourcePaths": ["source"]
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "dynamic",
 | 
				
			||||||
 | 
								"targetType": "dynamicLibrary",
 | 
				
			||||||
 | 
								"targetPath": "lib",
 | 
				
			||||||
 | 
								"sourcePaths": ["source"]
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "binary",
 | 
				
			||||||
 | 
								"targetType": "executable",
 | 
				
			||||||
 | 
								"targetPath": "bin",
 | 
				
			||||||
 | 
								"mainSourceFile": "test/app.d",
 | 
				
			||||||
 | 
								"sourcePaths": ["source", "test"]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										7
									
								
								dub.selections.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								dub.selections.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						"fileVersion": 1,
 | 
				
			||||||
 | 
						"versions": {
 | 
				
			||||||
 | 
							"arsd-official": "12.0.0",
 | 
				
			||||||
 | 
							"zstd": "0.2.1"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										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;
 | 
				
			||||||
							
								
								
									
										11
									
								
								test/app.d
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								test/app.d
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					import std.stdio;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import cdcdb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import std.file : read;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void main()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						auto cas = new CAS("/tmp/base.db");
 | 
				
			||||||
 | 
						cas.saveSnapshot(cast(ubyte[]) read("/tmp/text"));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										42
									
								
								tools/gen.d
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										42
									
								
								tools/gen.d
									
										
									
									
									
										Executable file
									
								
							| 
						 | 
					@ -0,0 +1,42 @@
 | 
				
			||||||
 | 
					#!/usr/bin/rdmd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import std.stdio : write, writef, writeln;
 | 
				
			||||||
 | 
					import std.random : Random, unpredictableSeed, uniform;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void main()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						enum N = 256;
 | 
				
			||||||
 | 
						ulong[N] gear;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto rng = Random(unpredictableSeed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool[ulong] seen;
 | 
				
			||||||
 | 
						ulong[] vals;
 | 
				
			||||||
 | 
						vals.reserve(N);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (vals.length < N)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const v = uniform!ulong(rng);
 | 
				
			||||||
 | 
							if (v in seen)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							seen[v] = true;
 | 
				
			||||||
 | 
							vals ~= v;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gear[] = vals[0 .. N];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						writeln("immutable ulong[256] gear = [");
 | 
				
			||||||
 | 
						foreach (i, v; gear)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (i % 4 == 0)
 | 
				
			||||||
 | 
								write("\t");
 | 
				
			||||||
 | 
							writef("0x%016x", v);
 | 
				
			||||||
 | 
							if (i != N - 1)
 | 
				
			||||||
 | 
								write(",");
 | 
				
			||||||
 | 
							if ((i + 1) % 4 == 0 || i == N - 1)
 | 
				
			||||||
 | 
								writeln();
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								write(" ");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						writeln("];");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue