| … | … | @@ -50,7 +50,7 @@ static const char* version_key = "*-Version-*"; |
| 50 | 50 | /* ================================================================ */ |
| 51 | 51 | // Constructors |
| 52 | 52 | |
| 53 | void MonoStorage::init(const char * uri) | |
| 53 | void MonoStorage::init(const char * uri, bool read_only) | |
| 54 | 54 | { |
| 55 | 55 | #define URIX_LEN (sizeof("monospace://") - 1) // Should be 12 |
| 56 | 56 | // We expect the URI to be for the form (note: three slashes) |
| … | … | @@ -64,8 +64,8 @@ void MonoStorage::init(const char * uri) |
| 64 | 64 | // Prefix for bloom filter -- first 2 chars. |
| 65 | 65 | // options.prefix_extractor.reset(rocksdb::NewFixedPrefixTransform(2)); |
| 66 | 66 | |
| 67 | // Create the file if it doesn't exist yet. | |
| 68 | options.create_if_missing = true; | |
| 67 | // Create the file if it doesn't exist yet (not in read-only mode). | |
| 68 | options.create_if_missing = not read_only; | |
| 69 | 69 | |
| 70 | 70 | // The primary consumer of disk and RAM in RocksDB are the `*.sst` |
| 71 | 71 | // files: each one is opened and memory-mapped. RocksDB does NOT |
| … | … | @@ -105,18 +105,27 @@ void MonoStorage::init(const char * uri) |
| 105 | 105 | #endif |
| 106 | 106 | |
| 107 | 107 | // Open the file. |
| 108 | rocksdb::Status s = rocksdb::DB::Open(options, file, &_rfile); | |
| 108 | rocksdb::Status s; | |
| 109 | if (read_only) | |
| 110 | s = rocksdb::DB::OpenForReadOnly(options, file, &_rfile); | |
| 111 | else | |
| 112 | s = rocksdb::DB::Open(options, file, &_rfile); | |
| 109 | 113 | |
| 110 | 114 | if (not s.ok()) |
| 111 | 115 | throw IOException(TRACE_INFO, "Can't open file: %s", |
| 112 | 116 | s.ToString().c_str()); |
| 113 | 117 | |
| 118 | _read_only = read_only; | |
| 119 | ||
| 114 | 120 | // Verify the version number. Version numbers are not currently used; |
| 115 | 121 | // this is for future-proofing future versions. |
| 116 | 122 | std::string version; |
| 117 | 123 | s = _rfile->Get(rocksdb::ReadOptions(), version_key, &version); |
| 118 | 124 | if (not s.ok()) |
| 119 | 125 | { |
| 126 | if (read_only) | |
| 127 | throw IOException(TRACE_INFO, | |
| 128 | "Cannot open read-only: DB has no version (not initialized)"); | |
| 120 | 129 | s = _rfile->Put(rocksdb::WriteOptions(), version_key, "1"); |
| 121 | 130 | } |
| 122 | 131 | else |
| … | … | @@ -130,6 +139,9 @@ void MonoStorage::init(const char * uri) |
| 130 | 139 | s = _rfile->Get(rocksdb::ReadOptions(), aid_key, &sid); |
| 131 | 140 | if (not s.ok()) |
| 132 | 141 | { |
| 142 | if (read_only) | |
| 143 | throw IOException(TRACE_INFO, | |
| 144 | "Cannot open read-only: DB has no aid (not initialized)"); | |
| 133 | 145 | _next_aid = 1; |
| 134 | 146 | sid = aidtostr(1); |
| 135 | 147 | s = _rfile->Put(rocksdb::WriteOptions(), aid_key, sid); |
| … | … | @@ -148,9 +160,11 @@ void MonoStorage::init(const char * uri) |
| 148 | 160 | throw IOException(TRACE_INFO, |
| 149 | 161 | "Cannot use MonoStorageNode to open multi-space storage '%s'\n", uri); |
| 150 | 162 | |
| 151 | printf("Mono: opened=%s\n", file.c_str()); | |
| 163 | printf("Mono: opened=%s%s\n", file.c_str(), read_only ? " (read-only)" : ""); | |
| 152 | 164 | printf("Mono: initial aid=%lu\n", _next_aid.load()); |
| 153 | 165 | |
| 166 | if (read_only) return; | |
| 167 | ||
| 154 | 168 | // Set up a SID for the TV predicate key. |
| 155 | 169 | // This must match what the AtomSpace is using. |
| 156 | 170 | // Tack on a leading colon, for convenience. |
| … | … | @@ -162,12 +176,20 @@ void MonoStorage::open() |
| 162 | 176 | { |
| 163 | 177 | // User might call us twice. If so, ignore the second call. |
| 164 | 178 | if (_rfile) return; |
| 165 | init(_name.c_str()); | |
| 179 | init(_name.c_str(), false); | |
| 180 | } | |
| 181 | ||
| 182 | void MonoStorage::open_read_only() | |
| 183 | { | |
| 184 | // User might call us twice. If so, ignore the second call. | |
| 185 | if (_rfile) return; | |
| 186 | init(_name.c_str(), true); | |
| 166 | 187 | } |
| 167 | 188 | |
| 168 | 189 | MonoStorage::MonoStorage(std::string uri) : |
| 169 | 190 | StorageNode(MONO_STORAGE_NODE, std::move(uri)), |
| 170 | 191 | _rfile(nullptr), |
| 192 | _read_only(false), | |
| 171 | 193 | _next_aid(0), |
| 172 | 194 | _unknown_type(false) |
| 173 | 195 | { |
| … | … | @@ -198,11 +220,15 @@ void MonoStorage::close() |
| 198 | 220 | { |
| 199 | 221 | if (nullptr == _rfile) return; |
| 200 | 222 | |
| 201 | logger().debug("Mono: storing final aid=%lu\n", _next_aid.load()); | |
| 202 | write_aid(); | |
| 223 | if (not _read_only) | |
| 224 | { | |
| 225 | logger().debug("Mono: storing final aid=%lu\n", _next_aid.load()); | |
| 226 | write_aid(); | |
| 227 | } | |
| 203 | 228 | delete _rfile; |
| 204 | 229 | _rfile = nullptr; |
| 205 | 230 | _next_aid = 0; |
| 231 | _read_only = false; | |
| 206 | 232 | } |
| 207 | 233 | |
| 208 | 234 | void MonoStorage::write_aid(void) |
| … | … | @@ -228,6 +254,7 @@ bool MonoStorage::connected(void) |
| 228 | 254 | /// |
| 229 | 255 | void MonoStorage::barrier() |
| 230 | 256 | { |
| 257 | if (_read_only) return; | |
| 231 | 258 | // belt and suspenders. |
| 232 | 259 | write_aid(); |
| 233 | 260 | } |
| … | … | @@ -51,10 +51,13 @@ class MonoStorage : public StorageNode |
| 51 | 51 | friend class MonoSatisfyingSet; |
| 52 | 52 | friend class MonoJoinCallback; |
| 53 | 53 | private: |
| 54 | void init(const char *); | |
| 54 | void init(const char *, bool read_only = false); | |
| 55 | 55 | std::string _uri; |
| 56 | 56 | rocksdb::DB* _rfile; |
| 57 | 57 | |
| 58 | // True if opened in read-only mode. | |
| 59 | bool _read_only; | |
| 60 | ||
| 58 | 61 | // unique ID's |
| 59 | 62 | std::atomic_uint64_t _next_aid; |
| 60 | 63 | uint64_t strtoaid(const std::string&) const; |
| … | … | @@ -104,6 +107,7 @@ class MonoStorage : public StorageNode |
| 104 | 107 | virtual ~MonoStorage(); |
| 105 | 108 | |
| 106 | 109 | void open(void); |
| 110 | void open_read_only(void); | |
| 107 | 111 | void close(void); |
| 108 | 112 | bool connected(void); // connection to DB is alive |
| 109 | 113 |
| … | … | @@ -50,7 +50,7 @@ static const char* version_key = "*-Version-*"; |
| 50 | 50 | /* ================================================================ */ |
| 51 | 51 | // Constructors |
| 52 | 52 | |
| 53 | void RocksStorage::init(const char * uri) | |
| 53 | void RocksStorage::init(const char * uri, bool read_only) | |
| 54 | 54 | { |
| 55 | 55 | #define URIX_LEN (sizeof("rocks://") - 1) // Should be 8 |
| 56 | 56 | // We expect the URI to be for the form (note: three slashes) |
| … | … | @@ -73,8 +73,8 @@ void RocksStorage::init(const char * uri) |
| 73 | 73 | // Prefix for bloom filter -- first 2 chars. |
| 74 | 74 | // options.prefix_extractor.reset(rocksdb::NewFixedPrefixTransform(2)); |
| 75 | 75 | |
| 76 | // Create the file if it doesn't exist yet. | |
| 77 | options.create_if_missing = true; | |
| 76 | // Create the file if it doesn't exist yet (not in read-only mode). | |
| 77 | options.create_if_missing = not read_only; | |
| 78 | 78 | |
| 79 | 79 | // The primary consumer of disk and RAM in RocksDB are the `*.sst` |
| 80 | 80 | // files: each one is opened and memory-mapped. RocksDB does NOT |
| … | … | @@ -114,12 +114,18 @@ void RocksStorage::init(const char * uri) |
| 114 | 114 | #endif |
| 115 | 115 | |
| 116 | 116 | // Open the file. |
| 117 | rocksdb::Status s = rocksdb::DB::Open(options, file, &_rfile); | |
| 117 | rocksdb::Status s; | |
| 118 | if (read_only) | |
| 119 | s = rocksdb::DB::OpenForReadOnly(options, file, &_rfile); | |
| 120 | else | |
| 121 | s = rocksdb::DB::Open(options, file, &_rfile); | |
| 118 | 122 | |
| 119 | 123 | if (not s.ok()) |
| 120 | 124 | throw IOException(TRACE_INFO, "Can't open file: %s", |
| 121 | 125 | s.ToString().c_str()); |
| 122 | 126 | |
| 127 | _read_only = read_only; | |
| 128 | ||
| 123 | 129 | // Does the file contain multiple atomspaces? |
| 124 | 130 | auto it = _rfile->NewIterator(rocksdb::ReadOptions()); |
| 125 | 131 | it->Seek("f@"); |
| … | … | @@ -137,6 +143,9 @@ void RocksStorage::init(const char * uri) |
| 137 | 143 | s = _rfile->Get(rocksdb::ReadOptions(), version_key, &version); |
| 138 | 144 | if (not s.ok()) |
| 139 | 145 | { |
| 146 | if (read_only) | |
| 147 | throw IOException(TRACE_INFO, | |
| 148 | "Cannot open read-only: DB has no version (not initialized)"); | |
| 140 | 149 | _rfile->Put(rocksdb::WriteOptions(), version_key, "2"); |
| 141 | 150 | } |
| 142 | 151 | else |
| … | … | @@ -148,7 +157,8 @@ void RocksStorage::init(const char * uri) |
| 148 | 157 | |
| 149 | 158 | // If it's version 1, and does not (yet) contain multiple |
| 150 | 159 | // spaces, it is safe to upgrade to version 2. |
| 151 | if (0 == version.compare("1") and not _multi_space) | |
| 160 | // Skip upgrade in read-only mode. | |
| 161 | if (not read_only and 0 == version.compare("1") and not _multi_space) | |
| 152 | 162 | _rfile->Put(rocksdb::WriteOptions(), version_key, "2"); |
| 153 | 163 | } |
| 154 | 164 | |
| … | … | @@ -157,6 +167,9 @@ void RocksStorage::init(const char * uri) |
| 157 | 167 | s = _rfile->Get(rocksdb::ReadOptions(), aid_key, &sid); |
| 158 | 168 | if (not s.ok()) |
| 159 | 169 | { |
| 170 | if (read_only) | |
| 171 | throw IOException(TRACE_INFO, | |
| 172 | "Cannot open read-only: DB has no aid (not initialized)"); | |
| 160 | 173 | _next_aid = 1; |
| 161 | 174 | sid = aidtostr(1); |
| 162 | 175 | s = _rfile->Put(rocksdb::WriteOptions(), aid_key, sid); |
| … | … | @@ -165,10 +178,12 @@ void RocksStorage::init(const char * uri) |
| 165 | 178 | _next_aid = strtoaid(sid) + 1; // next unused... |
| 166 | 179 | |
| 167 | 180 | // Informational prints. |
| 168 | printf("Rocks: opened=%s\n", file.c_str()); | |
| 181 | printf("Rocks: opened=%s%s\n", file.c_str(), read_only ? " (read-only)" : ""); | |
| 169 | 182 | printf("Rocks: DB-version=%s multi-space=%d initial aid=%lu\n", |
| 170 | 183 | get_version().c_str(), _multi_space, _next_aid.load()); |
| 171 | 184 | |
| 185 | if (read_only) return; | |
| 186 | ||
| 172 | 187 | // Set up a SID for the TV predicate key. |
| 173 | 188 | // This must match what the AtomSpace is using. |
| 174 | 189 | // Tack on a leading colon, for convenience. |
| … | … | @@ -180,13 +195,21 @@ void RocksStorage::open() |
| 180 | 195 | { |
| 181 | 196 | // User might call us twice. If so, ignore the second call. |
| 182 | 197 | if (_rfile) return; |
| 183 | init(_name.c_str()); | |
| 198 | init(_name.c_str(), false); | |
| 199 | } | |
| 200 | ||
| 201 | void RocksStorage::open_read_only() | |
| 202 | { | |
| 203 | // User might call us twice. If so, ignore the second call. | |
| 204 | if (_rfile) return; | |
| 205 | init(_name.c_str(), true); | |
| 184 | 206 | } |
| 185 | 207 | |
| 186 | 208 | RocksStorage::RocksStorage(std::string uri) : |
| 187 | 209 | StorageNode(ROCKS_STORAGE_NODE, std::move(uri)), |
| 188 | 210 | _rfile(nullptr), |
| 189 | 211 | _multi_space(false), |
| 212 | _read_only(false), | |
| 190 | 213 | _unknown_type(false), |
| 191 | 214 | _next_aid(0) |
| 192 | 215 | { |
| … | … | @@ -217,14 +240,18 @@ void RocksStorage::close() |
| 217 | 240 | { |
| 218 | 241 | if (nullptr == _rfile) return; |
| 219 | 242 | |
| 220 | logger().debug("Rocks: storing final aid=%lu\n", _next_aid.load()); | |
| 221 | write_aid(); | |
| 243 | if (not _read_only) | |
| 244 | { | |
| 245 | logger().debug("Rocks: storing final aid=%lu\n", _next_aid.load()); | |
| 246 | write_aid(); | |
| 247 | } | |
| 222 | 248 | delete _rfile; |
| 223 | 249 | _rfile = nullptr; |
| 224 | 250 | _next_aid = 0; |
| 225 | 251 | |
| 226 | 252 | // Invalidate the local cache. |
| 227 | 253 | _multi_space = false; |
| 254 | _read_only = false; | |
| 228 | 255 | _frame_map.clear(); |
| 229 | 256 | _fid_map.clear(); |
| 230 | 257 | _top_frames.clear(); |
| … | … | @@ -278,6 +305,7 @@ bool RocksStorage::connected(void) |
| 278 | 305 | /// |
| 279 | 306 | void RocksStorage::barrier(AtomSpace* as) |
| 280 | 307 | { |
| 308 | if (_read_only) return; | |
| 281 | 309 | // belt and suspenders. |
| 282 | 310 | write_aid(); |
| 283 | 311 | } |
| … | … | @@ -48,7 +48,7 @@ namespace opencog |
| 48 | 48 | class RocksStorage : public StorageNode |
| 49 | 49 | { |
| 50 | 50 | private: |
| 51 | void init(const char *); | |
| 51 | void init(const char *, bool read_only = false); | |
| 52 | 52 | std::string _uri; |
| 53 | 53 | rocksdb::DB* _rfile; |
| 54 | 54 | |
| … | … | @@ -58,6 +58,9 @@ class RocksStorage : public StorageNode |
| 58 | 58 | // True if file contains more than one atomspace. |
| 59 | 59 | bool _multi_space; |
| 60 | 60 | |
| 61 | // True if opened in read-only mode. | |
| 62 | bool _read_only; | |
| 63 | ||
| 61 | 64 | // Exception due to unknown Atom type. |
| 62 | 65 | bool _unknown_type; |
| 63 | 66 | |
| … | … | @@ -140,6 +143,7 @@ class RocksStorage : public StorageNode |
| 140 | 143 | virtual ~RocksStorage(); |
| 141 | 144 | |
| 142 | 145 | void open(void); |
| 146 | void open_read_only(void); | |
| 143 | 147 | void close(void); |
| 144 | 148 | bool connected(void); // connection to DB is alive |
| 145 | 149 |