Browse Source

Added LevelDB based backend

Eyck Jentzsch 1 year ago
parent
commit
2fd15aa76d
4 changed files with 510 additions and 7 deletions
  1. 7
    3
      incl/scc/scv_tr_db.h
  2. 6
    2
      src/CMakeLists.txt
  3. 2
    2
      src/scv_tr_binary/scv_tr_binary.cpp
  4. 495
    0
      src/scv_tr_ldb/scv_tr_ldb.cpp

+ 7
- 3
incl/scc/scv_tr_db.h View File

@@ -18,17 +18,21 @@
18 18
 #define _SCC_SCV_TR_DB_H_
19 19
 
20 20
 /**
21
- *
21
+ * initializes the infrastructure to use a SQLite based transaction recording database
22 22
  */
23 23
 void scv_tr_sqlite_init();
24 24
 /**
25
- *
25
+ *initializes the infrastructure to use a compressed text based transaction recording database
26 26
  */
27 27
 void scv_tr_compressed_init();
28 28
 
29 29
 /**
30
- *
30
+ *initializes the infrastructure to use a binary transaction recording database
31 31
  */
32 32
 void scv_tr_binary_init();
33
+/**
34
+ * initializes the infrastructure to use a LevelDB based transaction recording database
35
+ */
36
+void scv_tr_ldb_init();
33 37
 
34 38
 #endif /* _SCC_SCV_TR_DB_H_ */

+ 6
- 2
src/CMakeLists.txt View File

@@ -9,13 +9,17 @@ set(LIB_SOURCES
9 9
     jsoncpp.cpp
10 10
     configurer.cpp
11 11
     perf_estimator.cpp
12
+    scv_tr_binary/scv_tr_binary.cpp
12 13
 )
13 14
 
14
-if(SCV_FOUND)   
15
+if(SCV_FOUND)
15 16
     if(ZLIB_FOUND)
16 17
         set(LIB_SOURCES ${LIB_SOURCES} scv_tr_compressed.cpp)
17 18
     endif(ZLIB_FOUND)
18
-    set(LIB_SOURCES ${LIB_SOURCES} scv_tr_sqlite/scv_tr_sqlite.cpp scv_tr_sqlite/sqlite3.c scv_tr_binary/scv_tr_binary.cpp )
19
+    if(CONAN_LEVELDB_ROOT)
20
+	    set(LIB_SOURCES ${LIB_SOURCES} scv_tr_ldb/scv_tr_ldb.cpp )
21
+    endif()
22
+    set(LIB_SOURCES ${LIB_SOURCES} scv_tr_sqlite/scv_tr_sqlite.cpp scv_tr_sqlite/sqlite3.c )
19 23
 endif(SCV_FOUND)
20 24
 
21 25
 # Define two variables in order not to repeat ourselves.

+ 2
- 2
src/scv_tr_binary/scv_tr_binary.cpp View File

@@ -80,6 +80,8 @@ using namespace std;
80 80
 enum EventType { BEGIN, RECORD, END };
81 81
 using data_type = scv_extensions_if::data_type;
82 82
 // ----------------------------------------------------------------------------
83
+namespace {
84
+
83 85
 struct ByteBufferWriter {
84 86
     ByteBufferWriter(size_t reserve = 32) { buf.reserve(reserve); }
85 87
     template <typename T> ByteBufferWriter &append(const T &v) {
@@ -302,7 +304,6 @@ struct Database : Base {
302 304
     }
303 305
 };
304 306
 
305
-namespace {
306 307
 vector<vector<uint64_t> *> concurrencyLevel;
307 308
 Database *db;
308 309
 std::unordered_map<uint64_t, uint64_t> id2offset;
@@ -314,7 +315,6 @@ void dbCb(const scv_tr_db &_scv_tr_db, scv_tr_db::callback_reason reason, void *
314 315
     case scv_tr_db::CREATE:
315 316
         if ((_scv_tr_db.get_name() != nullptr) && (strlen(_scv_tr_db.get_name()) != 0)) fName = _scv_tr_db.get_name();
316 317
         try {
317
-            if (fName.size() < 5 || fName.find(".txb", fName.size() - 5) == string::npos) fName += ".txb";
318 318
             db = new Database(fName);
319 319
         } catch (...) {
320 320
             _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't open recording file");

+ 495
- 0
src/scv_tr_ldb/scv_tr_ldb.cpp View File

@@ -0,0 +1,495 @@
1
+/*******************************************************************************
2
+ * Copyright 2018 MINRES Technologies GmbH
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ *     http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *******************************************************************************/
16
+#include "leveldb/db.h"
17
+#include <json/json.h>
18
+
19
+#include <array>
20
+#include <cmath>
21
+#include <cstdio>
22
+#include <cstring>
23
+#include <fcntl.h>
24
+#include <iostream>
25
+#include <iomanip>
26
+#include <sstream>
27
+#include <stdexcept>
28
+#include <string>
29
+#include <unordered_map>
30
+#include <unordered_set>
31
+#include <vector>
32
+#include <cassert>
33
+// clang-format off
34
+#include "scv/scv_util.h"
35
+#include "scv/scv_introspection.h"
36
+#include "scv/scv_tr.h"
37
+// clang-format on
38
+// ----------------------------------------------------------------------------
39
+// ----------------------------------------------------------------------------
40
+using namespace std;
41
+using namespace leveldb;
42
+using namespace Json;
43
+
44
+#ifdef _MSC_VER
45
+#define scv_tr_TEXT_LLU "%I64u"
46
+#define scv_tr_TEXT_LLX "%I64x"
47
+#define scv_tr_TEXT_16LLX "%016I64x"
48
+#else
49
+#define scv_tr_TEXT_LLU "%llu"
50
+#define scv_tr_TEXT_LLX "%llx"
51
+#define scv_tr_TEXT_16LLX "%016lx"
52
+#endif
53
+
54
+// ----------------------------------------------------------------------------
55
+enum EventType { BEGIN, RECORD, END };
56
+const char* EventTypeStr[] = { "BEGIN", "RECORD", "END" };
57
+using data_type = scv_extensions_if::data_type;
58
+// ----------------------------------------------------------------------------
59
+namespace {
60
+
61
+struct Database {
62
+
63
+    Database(const string &name):key_len(1024) {
64
+        wbuilder.settings_["indentation"]="";
65
+        CharReaderBuilder::strictMode(&rbuilder.settings_);
66
+        Options options;
67
+        options.create_if_missing = true;
68
+        options.compression = kSnappyCompression;
69
+        DestroyDB(name, options);
70
+        if(!DB::Open(options, name, &db).ok())
71
+            throw runtime_error("Could not create database");
72
+        key_buf=new char[key_len];
73
+   }
74
+
75
+    ~Database(){
76
+        delete db;
77
+        delete key_buf;
78
+    }
79
+    /**
80
+     *
81
+     * @param key   the database key
82
+     * @param val   the JSON Value to write
83
+     */
84
+    inline bool writeEntry(string& key, Value& val){
85
+        return db->Put(write_options, Slice(key.c_str(), key.size()), writeString(wbuilder, val)).ok();
86
+    }
87
+    /**
88
+     *
89
+     * @param key   the database key
90
+     * @param val   the JSON Value to write
91
+     */
92
+    inline bool writeEntry(string&& key, Value& val){
93
+        return db->Put(write_options, Slice(key.c_str(), key.size()), writeString(wbuilder, val)).ok();
94
+    }
95
+    /**
96
+     *
97
+     * @param id    stream id
98
+     * @param name  stream name
99
+     * @param kind  stream kind
100
+     */
101
+    inline void writeStream(uint64_t id, string name, string kind) {
102
+        auto len = sprintf(key_buf, "s~" scv_tr_TEXT_16LLX, id);
103
+        Value node{objectValue};
104
+        node["id"]=id;
105
+        node["name"]=name;
106
+        node["kind"]=kind;
107
+        db->Put(write_options, Slice(key_buf, len), writeString(wbuilder, node));
108
+    }
109
+    /**
110
+     *
111
+     * @param id        generator id
112
+     * @param name
113
+     * @param stream
114
+     */
115
+    inline void writeGenerator(uint64_t id, string name, uint64_t stream) {
116
+        auto len = sprintf(key_buf, "sg~" scv_tr_TEXT_16LLX "~" scv_tr_TEXT_16LLX, stream, id);
117
+        Value node{objectValue};
118
+        node["id"]=id;
119
+        node["name"]=name;
120
+        node["stream"]=stream;
121
+        db->Put(write_options, Slice(key_buf, len), writeString(wbuilder, node));
122
+    }
123
+    /**
124
+     *
125
+     * @param id        transaction id
126
+     * @param generator
127
+     * @param concurrencyLevel
128
+     */
129
+    inline void writeTransaction(uint64_t id, uint64_t stream_id, uint64_t generator_id, uint64_t concurrencyLevel) {
130
+        Value val{objectValue};
131
+        val["id"]=id;
132
+        val["s"]=stream_id;
133
+        val["g"]=generator_id;
134
+        val["conc"]=concurrencyLevel;
135
+        db->Put(write_options, Slice(key_buf, sprintf(key_buf, "sgx~" scv_tr_TEXT_16LLX "~" scv_tr_TEXT_16LLX "~" scv_tr_TEXT_16LLX,
136
+                stream_id, generator_id, id)), "");
137
+        tx_lut[id]=val;
138
+        //db->Put(write_options, Slice(key, sprintf(key, "x~" scv_tr_TEXT_16LLX, id)), writeString(wbuilder, val));
139
+    }
140
+    /**
141
+     *
142
+     * @param id        transaction id
143
+     * @param streamid  stream transaction id
144
+     * @param type
145
+     * @param time
146
+     */
147
+    inline void writeTxTimepoint(uint64_t id, uint64_t streamid, EventType type, uint64_t time) {
148
+        string value;
149
+        Value node{arrayValue};
150
+        auto len = sprintf(key_buf, "st~" scv_tr_TEXT_16LLX "~" scv_tr_TEXT_16LLX "~%s", streamid, time, EventTypeStr[type]);
151
+        if (db->Get(read_options, key_buf, &value).ok() && rbuilder.newCharReader()->parse(value.data(), value.data()+value.size(), &node, nullptr)){
152
+            node[node.size()]= Value(id);
153
+        } else {
154
+            node[0u]= Value(id);
155
+        }
156
+        db->Put(write_options, Slice(key_buf, len), writeString(wbuilder, node));
157
+        updateTx(id, type, time);
158
+    }
159
+
160
+    inline void updateTx(uint64_t id, EventType type, uint64_t time){
161
+        static const char* typeStr[] = { "START_TIME", "", "END_TIME" };
162
+        auto& node = tx_lut[id];
163
+        node[typeStr[type]]=time;
164
+        if(type==END){
165
+            db->Put(write_options, Slice(key_buf, sprintf(key_buf, "x~" scv_tr_TEXT_16LLX, id)), writeString(wbuilder, node));
166
+            tx_lut.erase(id);
167
+        }
168
+    }
169
+
170
+    inline void updateTx(uint64_t id, Value&& val){
171
+        auto len = sprintf(key_buf, "x~" scv_tr_TEXT_16LLX, id);
172
+        auto& node = tx_lut[id];
173
+        auto& arrNode = node["attr"];
174
+        if(arrNode.isNull()){
175
+            Value newNode{arrayValue};
176
+            newNode.append(val);
177
+            node["attr"]=newNode;
178
+        } else {
179
+            arrNode.append(val);
180
+        }
181
+    }
182
+
183
+    /**
184
+     *
185
+     * @param id        transaction id
186
+     * @param event
187
+     * @param name
188
+     * @param type
189
+     * @param value
190
+     */
191
+    inline void writeAttribute(uint64_t id, EventType event, const string &name, data_type type, const string &value) {
192
+        Value val;
193
+        val["name"]=name;
194
+        val["type"]=type;
195
+        val["value"]=value;
196
+        val["assoc"]=event;
197
+        updateTx(id, move(val));
198
+    }
199
+    /**
200
+     *
201
+     * @param id        transaction id
202
+     * @param event
203
+     * @param name
204
+     * @param type
205
+     * @param value
206
+     */
207
+    inline void writeAttribute(uint64_t id, EventType event, const string &name, data_type type, uint64_t value) {
208
+        Value val;
209
+        val["name"]=name;
210
+        val["type"]=type;
211
+        val["value"]=value;
212
+        val["assoc"]=event;
213
+        updateTx(id, move(val));
214
+    }
215
+    /**
216
+     *
217
+     * @param id        transaction id
218
+     * @param event
219
+     * @param name
220
+     * @param type
221
+     * @param value
222
+     */
223
+    inline void writeAttribute(uint64_t id, EventType event, const string &name, data_type type, double value) {
224
+        Value val;
225
+        val["name"]=name;
226
+        val["type"]=type;
227
+        val["value"]=value;
228
+        val["assoc"]=event;
229
+        updateTx(id, move(val));
230
+   }
231
+    /**
232
+     *
233
+     * @param name
234
+     * @param sink_id
235
+     * @param src_id
236
+     */
237
+    inline void writeRelation(const string &name, uint64_t sink_id, uint64_t src_id) {
238
+        if(key_len<(name.size()+32+5)){ //reallocate buffer if needed, making sure no buffer overflow
239
+            delete key_buf;
240
+            key_len=name.size()+32+5;
241
+            key_buf = new char[key_len];
242
+        }
243
+        db->Put(write_options, Slice(key_buf, sprintf(key_buf, "ro~" scv_tr_TEXT_16LLX "~" scv_tr_TEXT_16LLX "~%s", src_id, sink_id, name.c_str())), "");
244
+        db->Put(write_options, Slice(key_buf, sprintf(key_buf, "ri~" scv_tr_TEXT_16LLX "~" scv_tr_TEXT_16LLX "~%s", sink_id, src_id, name.c_str())), "");
245
+    }
246
+private:
247
+    DB* db;
248
+    ReadOptions read_options;
249
+    WriteOptions write_options;
250
+    char* key_buf;
251
+    size_t key_len;
252
+    StreamWriterBuilder wbuilder;
253
+    CharReaderBuilder rbuilder;
254
+    unordered_map<uint64_t, Value> tx_lut;
255
+};
256
+
257
+vector<vector<uint64_t>> concurrencyLevel;
258
+
259
+Database *db;
260
+
261
+void dbCb(const scv_tr_db &_scv_tr_db, scv_tr_db::callback_reason reason, void *data) {
262
+    // This is called from the scv_tr_db ctor.
263
+    static string fName("DEFAULT_scv_tr_sqlite");
264
+    switch (reason) {
265
+    case scv_tr_db::CREATE:
266
+        if ((_scv_tr_db.get_name() != nullptr) && (strlen(_scv_tr_db.get_name()) != 0))
267
+            fName = _scv_tr_db.get_name();
268
+        try {
269
+            db = new Database(fName);
270
+            Value val{objectValue};
271
+            val["resolution"]=(long)(sc_get_time_resolution().to_seconds() * 1e15);
272
+            db->writeEntry("__config", val);
273
+        } catch (runtime_error& e) {
274
+            _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, e.what());
275
+        } catch (...) {
276
+            _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't open recording file");
277
+        }
278
+        break;
279
+    case scv_tr_db::DELETE:
280
+        try {
281
+            delete db;
282
+        } catch (...) {
283
+            _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't close recording file");
284
+        }
285
+        break;
286
+    default:
287
+        _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Unknown reason in scv_tr_db callback");
288
+    }
289
+}
290
+// ----------------------------------------------------------------------------
291
+void streamCb(const scv_tr_stream &s, scv_tr_stream::callback_reason reason, void *data) {
292
+    if (reason == scv_tr_stream::CREATE) {
293
+        try {
294
+            db->writeStream(s.get_id(), s.get_name(), s.get_stream_kind());
295
+        } catch (runtime_error &e) {
296
+            _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create stream");
297
+        }
298
+    }
299
+}
300
+// ----------------------------------------------------------------------------
301
+void recordAttribute(uint64_t id, EventType event, const string &name, data_type type, const string &value) {
302
+    try {
303
+        db->writeAttribute(id, event, name, type, value);
304
+    } catch (runtime_error &e) {
305
+        _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create attribute entry");
306
+    }
307
+}
308
+// ----------------------------------------------------------------------------
309
+void recordAttribute(uint64_t id, EventType event, const string &name, data_type type, long long value) {
310
+    try {
311
+        db->writeAttribute(id, event, name, type, static_cast<uint64_t>(value));
312
+    } catch (runtime_error &e) {
313
+        _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create attribute entry");
314
+    }
315
+}
316
+// ----------------------------------------------------------------------------
317
+inline void recordAttribute(uint64_t id, EventType event, const string &name, data_type type, double value) {
318
+    try {
319
+        db->writeAttribute(id, event, name, type, value);
320
+    } catch (runtime_error &e) {
321
+        _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create attribute entry");
322
+    }
323
+}
324
+// ----------------------------------------------------------------------------
325
+void recordAttributes(uint64_t id, EventType eventType, string &prefix, const scv_extensions_if *my_exts_p) {
326
+    if (my_exts_p == nullptr) return;
327
+    string name;
328
+    if (prefix == "") {
329
+        name = my_exts_p->get_name();
330
+    } else {
331
+        if ((my_exts_p->get_name() == nullptr) || (strlen(my_exts_p->get_name()) == 0)) {
332
+            name = prefix;
333
+        } else {
334
+            name = prefix + "." + my_exts_p->get_name();
335
+        }
336
+    }
337
+    if (name == "") name = "<unnamed>";
338
+    switch (my_exts_p->get_type()) {
339
+    case scv_extensions_if::RECORD: {
340
+        int num_fields = my_exts_p->get_num_fields();
341
+        if (num_fields > 0) {
342
+            for (int field_counter = 0; field_counter < num_fields; field_counter++) {
343
+                const scv_extensions_if *field_data_p = my_exts_p->get_field(field_counter);
344
+                recordAttributes(id, eventType, prefix, field_data_p);
345
+            }
346
+        }
347
+    } break;
348
+    case scv_extensions_if::ENUMERATION:
349
+        recordAttribute(id, eventType, name, scv_extensions_if::ENUMERATION,
350
+                        my_exts_p->get_enum_string((int)(my_exts_p->get_integer())));
351
+        break;
352
+    case scv_extensions_if::BOOLEAN:
353
+        recordAttribute(id, eventType, name, scv_extensions_if::BOOLEAN, my_exts_p->get_bool() ? "TRUE" : "FALSE");
354
+        break;
355
+    case scv_extensions_if::INTEGER:
356
+    case scv_extensions_if::FIXED_POINT_INTEGER:
357
+        recordAttribute(id, eventType, name, scv_extensions_if::INTEGER, my_exts_p->get_integer());
358
+        break;
359
+    case scv_extensions_if::UNSIGNED:
360
+        recordAttribute(id, eventType, name, scv_extensions_if::UNSIGNED, my_exts_p->get_integer());
361
+        break;
362
+    case scv_extensions_if::POINTER:
363
+        recordAttribute(id, eventType, name, scv_extensions_if::POINTER, (long long)my_exts_p->get_pointer());
364
+        break;
365
+    case scv_extensions_if::STRING:
366
+        recordAttribute(id, eventType, name, scv_extensions_if::STRING, my_exts_p->get_string());
367
+        break;
368
+    case scv_extensions_if::FLOATING_POINT_NUMBER:
369
+        recordAttribute(id, eventType, name, scv_extensions_if::FLOATING_POINT_NUMBER, my_exts_p->get_double());
370
+        break;
371
+    case scv_extensions_if::BIT_VECTOR: {
372
+        sc_bv_base tmp_bv(my_exts_p->get_bitwidth());
373
+        my_exts_p->get_value(tmp_bv);
374
+        recordAttribute(id, eventType, name, scv_extensions_if::BIT_VECTOR, tmp_bv.to_string());
375
+    } break;
376
+    case scv_extensions_if::LOGIC_VECTOR: {
377
+        sc_lv_base tmp_lv(my_exts_p->get_bitwidth());
378
+        my_exts_p->get_value(tmp_lv);
379
+        recordAttribute(id, eventType, name, scv_extensions_if::LOGIC_VECTOR, tmp_lv.to_string());
380
+    } break;
381
+    case scv_extensions_if::ARRAY:
382
+        for (int array_elt_index = 0; array_elt_index < my_exts_p->get_array_size(); array_elt_index++) {
383
+            const scv_extensions_if *field_data_p = my_exts_p->get_array_elt(array_elt_index);
384
+            recordAttributes(id, eventType, prefix, field_data_p);
385
+        }
386
+        break;
387
+    default: {
388
+        array<char, 100> tmpString;
389
+        sprintf(tmpString.data(), "Unsupported attribute type = %d", my_exts_p->get_type());
390
+        _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, tmpString.data());
391
+    }
392
+    }
393
+}
394
+// ----------------------------------------------------------------------------
395
+void generatorCb(const scv_tr_generator_base &g, scv_tr_generator_base::callback_reason reason, void *data) {
396
+    if (reason == scv_tr_generator_base::CREATE && db) {
397
+        try {
398
+            db->writeGenerator(g.get_id(), g.get_name(), g.get_scv_tr_stream().get_id());
399
+        } catch (runtime_error &e) {
400
+            _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create generator entry");
401
+        }
402
+    }
403
+}
404
+// ----------------------------------------------------------------------------
405
+void transactionCb(const scv_tr_handle &t, scv_tr_handle::callback_reason reason, void *data) {
406
+    if (!db) return;
407
+    if (t.get_scv_tr_stream().get_scv_tr_db() == nullptr) return;
408
+    if (t.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false) return;
409
+
410
+    uint64_t id = t.get_id();
411
+    uint64_t streamId = t.get_scv_tr_stream().get_id();
412
+    vector<uint64_t>::size_type concurrencyIdx;
413
+    const scv_extensions_if *my_exts_p;
414
+    switch (reason) {
415
+    case scv_tr_handle::BEGIN: {
416
+        try {
417
+            if (concurrencyLevel.size() <= streamId) concurrencyLevel.resize(streamId + 1);
418
+            vector<uint64_t>& levels = concurrencyLevel.at(streamId);
419
+            for (concurrencyIdx = 0; concurrencyIdx < levels.size(); ++concurrencyIdx) //find a free slot
420
+                if (levels[concurrencyIdx] == 0) break;
421
+            if (concurrencyIdx == levels.size())
422
+                levels.push_back(id);
423
+            else
424
+                levels[concurrencyIdx] = id;
425
+            db->writeTransaction(id, t.get_scv_tr_stream().get_id(), t.get_scv_tr_generator_base().get_id(), concurrencyIdx);
426
+            db->writeTxTimepoint(id, t.get_scv_tr_stream().get_id(), BEGIN, t.get_begin_sc_time().value());
427
+        } catch (runtime_error &e) {
428
+            _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, e.what());
429
+        }
430
+        my_exts_p = t.get_begin_exts_p();
431
+        if (my_exts_p == nullptr) my_exts_p = t.get_scv_tr_generator_base().get_begin_exts_p();
432
+        if (my_exts_p) {
433
+            string tmp_str = t.get_scv_tr_generator_base().get_begin_attribute_name()
434
+                                 ? t.get_scv_tr_generator_base().get_begin_attribute_name()
435
+                                 : "";
436
+            recordAttributes(id, BEGIN, tmp_str, my_exts_p);
437
+        }
438
+    } break;
439
+    case scv_tr_handle::END: {
440
+        my_exts_p = t.get_end_exts_p();
441
+        if (my_exts_p == nullptr) my_exts_p = t.get_scv_tr_generator_base().get_end_exts_p();
442
+        if (my_exts_p) {
443
+            string tmp_str = t.get_scv_tr_generator_base().get_end_attribute_name()
444
+                                 ? t.get_scv_tr_generator_base().get_end_attribute_name()
445
+                                 : "";
446
+            recordAttributes(t.get_id(), END, tmp_str, my_exts_p);
447
+        }
448
+        try {
449
+            db->writeTxTimepoint(id, t.get_scv_tr_stream().get_id(), END, t.get_end_sc_time().value());
450
+            vector<uint64_t>& levels = concurrencyLevel[streamId];
451
+            for (concurrencyIdx = 0; concurrencyIdx < levels.size(); ++concurrencyIdx)
452
+                if (levels[concurrencyIdx] == id) break;
453
+            if (concurrencyIdx == levels.size())
454
+                levels.push_back(0);
455
+            else
456
+                levels[concurrencyIdx] = 0;
457
+        } catch (runtime_error &e) {
458
+            _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create transaction end");
459
+        }
460
+    } break;
461
+    default:;
462
+    }
463
+}
464
+// ----------------------------------------------------------------------------
465
+void attributeCb(const scv_tr_handle &t, const char *name, const scv_extensions_if *ext, void *data) {
466
+    if (!db) return;
467
+    if (t.get_scv_tr_stream().get_scv_tr_db() == nullptr) return;
468
+    if (t.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false) return;
469
+    string tmp_str(name == nullptr ? "" : name);
470
+    recordAttributes(t.get_id(), RECORD, tmp_str, ext);
471
+}
472
+// ----------------------------------------------------------------------------
473
+void relationCb(const scv_tr_handle &tr_1, const scv_tr_handle &tr_2, void *data,
474
+                scv_tr_relation_handle_t relation_handle) {
475
+    if (!db) return;
476
+    if (tr_1.get_scv_tr_stream().get_scv_tr_db() == nullptr) return;
477
+    if (tr_1.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false) return;
478
+    try {
479
+        db->writeRelation(tr_1.get_scv_tr_stream().get_scv_tr_db()->get_relation_name(relation_handle), tr_1.get_id(),
480
+                          tr_2.get_id());
481
+    } catch (runtime_error &e) {
482
+        _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create transaction relation");
483
+    }
484
+}
485
+} // namespace
486
+// ----------------------------------------------------------------------------
487
+void scv_tr_ldb_init() {
488
+    scv_tr_db::register_class_cb(dbCb);
489
+    scv_tr_stream::register_class_cb(streamCb);
490
+    scv_tr_generator_base::register_class_cb(generatorCb);
491
+    scv_tr_handle::register_class_cb(transactionCb);
492
+    scv_tr_handle::register_record_attribute_cb(attributeCb);
493
+    scv_tr_handle::register_relation_cb(relationCb);
494
+}
495
+// ----------------------------------------------------------------------------