Stroika Library 3.0d23
 
Loading...
Searching...
No Matches
LocalDocumentDB.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Database_Document_LocalDocumentDB_h_
5#define _Stroika_Foundation_Database_Document_LocalDocumentDB_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <filesystem>
10#include <optional>
11
13#include "Stroika/Foundation/Containers/Mapping.h"
14#include "Stroika/Foundation/Containers/Sequence.h"
24
25/**
26 * \file
27 *
28 * \note Code-Status: <a href="Code-Status.md#Alpha">Alpha</a>
29 *
30 * LocalDocumentDB is a (typically filesystem, but can be RAM based) simple implementation of the DocumentDB
31 * API. You can use this to debug/test, and possibly for limited, or embedded, small scale uses.
32 *
33 * Advantages:
34 * - Small, simple, easy to review code, and understand API
35 * - Very small dependency footprint
36 * - Lets you pick data format to store (when storing to disk), JSON, or BSON, or whatever you have
37 * serializers/de-serializers for.
38 *
39 * Disadvantages
40 * - Performance on larger scale
41 * - Transactions NYI
42 */
43
44namespace Stroika::Foundation::Database::Document::LocalDocumentDB {
45
46 using namespace Database::Document::Connection;
47
48 class IRep;
49
50 /**
51 * These are options used to create a database Connection::Ptr object (with Connection::New).
52 *
53 * Since this is also how you create a database, in a sense, its those options too.
54 */
55 struct Options final : Database::Document::Connection::Options {
56
57 /**
58 * \brief use eInternallySynchronized to make letter internally synchronized
59 *
60 * \note this refers to in-process synchronization. Future flags/fields/options will be needed
61 * in other impls to assure cross-process synchronization (not sure if even appropriate for this impl but maybe something simple with flock).
62 *
63 * \note if set eNotKnownInternallySynchronized (the default), in debug mode, the system uses AssertExternallySynchronizedMutex
64 * to check for unsafe thread usage.
65 */
66 Execution::InternallySynchronized fInternallySynchronizedLetter{Execution::eNotKnownInternallySynchronized};
67
68 /**
69 * @todo add options like max ram, max # objects?
70 */
71 struct MemoryStorage final {};
72
73 /**
74 * @todo add options like caching (support external process sync/flock)
75 */
76 struct SingleFileStorage final {
77 /**
78 * Where the file is stored.
79 */
80 filesystem::path fFile;
81
82 /**
83 * If true, the database connection will ignore any existing file (not read it). Either way
84 * a new file will be created (whether one was there before or not).
85 */
86 bool fForceCreateNew{false};
87
88 /**
89 * If true, the file will be read (unless fForceCreateNew is set, and it can), but will never be written to disk.
90 * (just cached in memory).
91 */
92 bool fReadOnly{false};
93
94 /**
95 * If true, each modification causes a write. If false, the implmentation MAY buffer writes (or may write thruough).
96 * The caller can always trigger a write by calling Flush(), or destorying the connection.
97 */
98 bool fFlushOnEachWrite{false};
99
100#if qStroika_Foundation_Common_Platform_Windows
101 /**
102 * \see IO::FileSystem::ThroughTmpFileWriter::fRetryOnSharingViolationFor
103 */
104 optional<Time::DurationSeconds> fRetryOnSharingViolationFor;
105#endif
106
107 /**
108 * Extension point so we can switch to writing files as BSON, msgpack, or some such...
109 */
110 tuple<DataExchange::Variant::Reader, DataExchange::Variant::Writer> fSerialization{DataExchange::Variant::JSON::Reader{},
112 };
113
114 /**
115 * @todo add options like caching (support external process sync/flock)
116 */
117 struct DirectoryFileStorage final {
118 /**
119 * The directory where the files are stored.
120 */
121 filesystem::path fRoot;
122
123 /**
124 * If true, the database connection will ignore any existing file (not read it). Either way
125 * a new file will be created (whether one was there before or not).
126 */
127 bool fForceCreateNew{false};
128
129#if qStroika_Foundation_Common_Platform_Windows
130 /**
131 * \see IO::FileSystem::ThroughTmpFileWriter::fRetryOnSharingViolationFor
132 */
133 optional<Time::DurationSeconds> fRetryOnSharingViolationFor;
134#endif
135
136 /**
137 * Extension point so we can switch to writing files as BSON, msgpack, or some such...
138 */
139 tuple<DataExchange::Variant::Reader, DataExchange::Variant::Writer> fSerialization{DataExchange::Variant::JSON::Reader{},
141 };
142
143 /**
144 *
145 */
146 variant<MemoryStorage, SingleFileStorage, DirectoryFileStorage> fStorage;
147 };
148
149 /**
150 *
151 */
152 class Ptr : public Database::Document::Connection::Ptr {
153 private:
154 using inherited = Database::Document::Connection::Ptr;
155
156 public:
157 /**
158 */
159 Ptr (const Ptr& src);
160 Ptr (const shared_ptr<IRep>& src);
161 Ptr (nullptr_t = nullptr) noexcept;
162
163 public:
164 ~Ptr () = default;
165
166 public:
167 /**
168 */
169 nonvirtual Ptr& operator= (const Ptr& src);
170 nonvirtual Ptr& operator= (Ptr&& src) noexcept;
171
172 public:
173 /**
174 */
175 nonvirtual IRep* operator->() const noexcept;
176
177 public:
178 /**
179 * If the database is configured to buffer writes, this forces a flush of all buffered writes to the underlying storage (e.g. disk).
180 * It may do nothing.
181 */
182 nonvirtual void Flush () const;
183 };
184
185 /**
186 * \brief create an LocalDocumentDB database (and connection) object, guided by argument Options.
187 *
188 * \par Example Usage
189 * \code
190 * // In memory DB can be used by multiple threads
191 * Connection::Ptr internallySynchronizedMemoryDBConnection = LocalDocumentDB::New (LocalDocumentDB::Options{
192 * .fInternallySynchronizedLetter = eInternallySynchronized, .fStorage = LocalDocumentDB::Options::MemoryStorage{}});
193 * \endcode
194 *
195 * \par Example Usage
196 * \code
197 * // only json file read-only using DocumentDB API
198 * Connection::Ptr copyFrom = LocalDocumentDB::New (LocalDocumentDB::Options{
199 * .fStorage = LocalDocumentDB::Options::SingleFileStorage{.fFile = loadFromStartupFile, .fReadOnly = true}});
200 * \endcode
201 *
202 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety-For-Envelope-But-Ambiguous-Thread-Safety-For-Letter">C++-Standard-Thread-Safety-For-Envelope-But-Ambiguous-Thread-Safety-For-Letter/a>
203 * the internal synchronization of the resulting letter object is controlled by Options::fInternallySynchronizedLetter
204 *
205 */
206 Ptr New (const Options& options);
207
208 /**
209 */
210 class IRep : public Database::Document::Connection::IRep {
211 public:
212 /**
213 * If the database is configured to buffer writes, this forces a flush of all buffered writes to the underlying storage (e.g. disk).
214 * It may do nothing.
215 */
216 virtual void Flush () = 0;
217
218 private:
219 friend class Ptr;
220 };
221
222}
223
224/*
225 ********************************************************************************
226 ***************************** Implementation Details ***************************
227 ********************************************************************************
228 */
229#include "LocalDocumentDB.inl"
230
231#endif /*_Stroika_Foundation_Database_Document_LocalDocumentDB_h_*/
tuple< DataExchange::Variant::Reader, DataExchange::Variant::Writer > fSerialization
tuple< DataExchange::Variant::Reader, DataExchange::Variant::Writer > fSerialization
Execution::InternallySynchronized fInternallySynchronizedLetter
use eInternallySynchronized to make letter internally synchronized