Lightweight 0.20261105.1
Loading...
Searching...
No Matches
Restore.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5#include "../SqlConnection.hpp"
6#include "Common.hpp"
7#include "RestoreFailureLog.hpp"
8#include "SqlBackup.hpp"
9
10#include <atomic>
11#include <cstdint>
12#include <deque>
13#include <expected>
14#include <map>
15#include <memory>
16#include <mutex>
17#include <optional>
18#include <set>
19#include <string>
20#include <vector>
21
22#if defined(__clang__)
23 #pragma clang diagnostic push
24 #pragma clang diagnostic ignored "-Wnullability-extension"
25#endif
26#include <zip.h>
27#if defined(__clang__)
28 #pragma clang diagnostic pop
29#endif
30
31namespace Lightweight::SqlBackup::detail
32{
33
34/// Data required to restore a single chunk to the database.
35struct RestoreChunkInfo
36{
37 std::string tableName;
38 std::string chunkPath;
39 std::vector<uint8_t> content;
40 TableInfo const* tableInfo {};
41 std::optional<size_t> displayTotal;
42 bool isEndOfStream {}; ///< True when queue is empty and no more data is available.
43};
44
45/// Error information from FetchNextRestoreChunk.
46struct FetchChunkError
47{
48 std::string tableName; // Empty if table could not be determined
49 std::string message;
50};
51
52/// The tables a chunk of which failed to restore, shared by all restore workers.
53struct FailedTables
54{
55 /// Records @p tableName as failed.
56 /// @param tableName The table.
57 /// @return `true` if the table was not failed before, i.e. for exactly one caller per table.
58 bool Add(std::string const& tableName)
59 {
60 auto const lock = std::scoped_lock(_mutex);
61 return _names.insert(tableName).second;
62 }
63
64 /// Tests whether @p tableName has failed.
65 /// @param tableName The table.
66 /// @return `true` once a chunk of the table failed.
67 [[nodiscard]] bool Contains(std::string const& tableName) const
68 {
69 auto const lock = std::scoped_lock(_mutex);
70 return _names.contains(tableName);
71 }
72
73 private:
74 mutable std::mutex _mutex;
75 std::set<std::string> _names;
76};
77
78/// The result of restoring one chunk.
79enum class ChunkRestoreOutcome : std::uint8_t
80{
81 /// Every row of the chunk was restored as it is.
82 Restored,
83 /// The chunk was processed, but some of its rows failed, some of its values were set to NULL, or
84 /// (part of) its content could not be read; the failures are in the restore's failure log.
85 RestoredWithFailures,
86 /// An error that affects every row (e.g. the driver cannot bind a column) fails the table: its
87 /// remaining chunks are skipped, the worker goes on.
88 TableFailed,
89 /// The connection was lost and could not be re-established within the retry budget: the worker stops.
90 WorkerFailed,
91};
92
93/// Returns the schema a table is restored into: the one the archive records for it, else @p defaultSchema.
94/// @param info The table.
95/// @param defaultSchema The schema the restore targets.
96[[nodiscard]] inline std::string TargetSchema(TableInfo const& info, std::string const& defaultSchema)
97{
98 return info.schema.empty() ? defaultSchema : info.schema;
99}
100
101/// Returns a table's name in the database: the name the archive records, else its key in the table map.
102/// @param key The table's key in the table map.
103/// @param info The table.
104[[nodiscard]] inline std::string TargetTableName(std::string const& key, TableInfo const& info)
105{
106 return info.name.empty() ? key : info.name;
107}
108
109/// Context for restore operations, shared between restore workers.
110struct RestoreContext
111{
112 SqlConnectionString connectionString;
113 std::string schema;
114 std::map<std::string, TableInfo> const& tableMap;
115 /// The archive folder of each table's chunks (see @ref ChunkDirectory) -> the table's key in @ref tableMap.
116 std::map<std::string, std::string> const& chunkTables;
117 std::deque<ZipEntryInfo>& dataQueue;
118 zip_t* zip;
119 std::mutex& queueMutex;
120 std::mutex& fileMutex;
121 ProgressManager& progress;
122 std::map<std::string, std::shared_ptr<std::atomic<size_t>>> tableProgress;
123 std::map<std::string, std::shared_ptr<std::atomic<size_t>>> chunksProcessed; // Per-table processed chunk count
124 std::map<std::string, size_t> totalChunks; // Per-table total chunk count
125 /// Per table: the rows committed to the target, which a failed table's failed row count excludes.
126 std::map<std::string, std::shared_ptr<std::atomic<std::uint64_t>>> committedRows;
127 std::map<std::string, std::string> const* checksums; // entryName -> expected SHA-256 hash (optional)
128 RetrySettings const& retrySettings;
129 RestoreSettings restoreSettings;
130 /// Tables whose remaining chunks are skipped because one of their chunks failed.
131 FailedTables& failedTables;
132 /// Collects what fails to restore, shared by all restore workers.
133 RestoreFailureLog& failureLog;
134 /// The DBMS the archive was taken from (`server.name` in its metadata); UNKNOWN if not recorded.
135 SqlServerType sourceServerType = SqlServerType::UNKNOWN;
136};
137
138/// Counts one more chunk of @p tableName as processed; once all are, reports the table's single
139/// terminal progress update.
140///
141/// The update is `Finished`, or `Error` naming the counts (`"<n> rows failed, <m> cells set to
142/// NULL"`, followed by the table failure and unreadable chunks, if any) when the failure log holds
143/// failures of the table. Every failure of the table's chunks must be recorded before this is called.
144///
145/// @param ctx The restore context.
146/// @param tableName The name of the table being restored.
147void IncrementChunkCounter(RestoreContext& ctx, std::string const& tableName);
148
149/// Sets the failed row count of @p tableName, if it failed as a whole, to its rows that are not in
150/// the target: its archived row count minus the rows committed for it. Idempotent; called once the
151/// table's chunks are processed, or once the workers stopped.
152///
153/// @param ctx The restore context.
154/// @param tableName The table.
155void SettleFailedTableRows(RestoreContext& ctx, std::string const& tableName);
156
157/// Fetches the next chunk from the restore queue.
158///
159/// Handles dequeuing from the work queue, reading zip entry content,
160/// path parsing to extract table name, and checksum verification.
161///
162/// @param ctx The restore context.
163/// @return The chunk info on success (with isEndOfStream=true when queue is empty), or error details on failure.
164std::expected<RestoreChunkInfo, FetchChunkError> FetchNextRestoreChunk(RestoreContext& ctx);
165
166/// Restores chunk data to the database with retry logic.
167///
168/// The chunk is first inserted in one transaction, committed after its last batch. A row the
169/// database rejects, or a value that does not convert, rolls that transaction back; the chunk is then
170/// replayed with each batch committing on its own. There, a batch the database rejects is rolled back
171/// and its failing rows are isolated (see InsertIsolatingFailures()) and recorded in the failure log;
172/// the other rows are inserted. After a transient error the worker reconnects and resumes after the
173/// rows already committed, so no row is inserted twice. Also handles MSSQL identity inserts and
174/// progress tracking.
175///
176/// @param ctx The restore context.
177/// @param workerConn The database connection.
178/// @param chunk The chunk data to restore.
179/// @param batchCapacity The batch size for bulk inserts.
180/// @return How the chunk ended; see ChunkRestoreOutcome.
181ChunkRestoreOutcome RestoreChunkData(RestoreContext& ctx,
182 SqlConnection& workerConn,
183 RestoreChunkInfo const& chunk,
184 size_t batchCapacity);
185
186/// Worker function that processes chunks from the restore queue.
187///
188/// This function processes restore chunks from the shared queue, using the helper functions
189/// FetchNextRestoreChunk() for I/O operations and RestoreChunkData() for database operations.
190/// Once a table fails, its remaining chunks are skipped. A chunk that cannot be fetched (e.g. a
191/// checksum mismatch) is recorded as unreadable and skipped. Only ChunkRestoreOutcome::WorkerFailed
192/// stops this worker; the chunk it failed on is not counted as processed, so its table shows as
193/// incomplete.
194///
195/// @param ctx The restore context containing queue, progress tracking, and settings.
196/// @param workerConn The database connection for this worker.
197void RestoreWorker(RestoreContext ctx, SqlConnection& workerConn);
198
199/// Restores indexes for all tables after data has been restored.
200///
201/// This function creates indexes that were backed up in the metadata.
202/// It is called after FK constraints are restored to ensure indexes are created
203/// on the final table structure.
204///
205/// @param connectionString The connection string to use.
206/// @param schema The schema name.
207/// @param tableMap Map of table names to their metadata including indexes.
208/// @param progress Progress manager for reporting status.
209LIGHTWEIGHT_API void RestoreIndexes(SqlConnectionString const& connectionString,
210 std::string const& schema,
211 std::map<std::string, TableInfo> const& tableMap,
212 ProgressManager& progress);
213
214/// Applies foreign key constraints to all tables after data has been restored.
215///
216/// This function is called after data restoration to add FK constraints that
217/// were not included in the initial CREATE TABLE statements.
218///
219/// @param connectionString The connection string to use.
220/// @param schema The schema name.
221/// @param tableMap Map of table names to their metadata including foreign keys.
222/// @param progress Progress manager for reporting status.
223LIGHTWEIGHT_API void ApplyDatabaseConstraints(SqlConnectionString const& connectionString,
224 std::string const& schema,
225 std::map<std::string, TableInfo> const& tableMap,
226 ProgressManager& progress);
227
228/// Recreates the database schema by dropping and creating tables from the backup metadata.
229///
230/// This function creates tables in dependency order for SQLite (to satisfy FK constraints on CREATE),
231/// or alphabetically for other databases (where FKs are added via ALTER TABLE later). The schemas
232/// that a multi-schema archive records are created first unless they exist.
233///
234/// @param connectionString The connection string for the target database.
235/// @param schema The database schema name to create tables in.
236/// @param tableMap Map of table names to their metadata from the backup.
237/// @param progress Progress manager for reporting errors and status updates.
238/// @return Set of table names that were successfully created. Tables that fail to create
239/// (e.g., due to type incompatibilities) are excluded from the returned set,
240/// allowing the caller to skip data restoration for those tables.
241LIGHTWEIGHT_API std::set<std::string> RecreateDatabaseSchema(SqlConnectionString const& connectionString,
242 std::string const& schema,
243 std::map<std::string, TableInfo> const& tableMap,
244 ProgressManager& progress);
245
246} // namespace Lightweight::SqlBackup::detail