Coverage report: /development/source/library/org/datagraph/spocq-shard/src/core/rlmdb/spocq-classes.lisp

KindCoveredAll%
expression31118 26.3
branch04 0.0
Key
Not instrumented
Conditionalized out
Executed
Not executed
 
Both branches taken
One branch taken
Neither branch taken
1
 ;;; -*- Mode: lisp; Syntax: ansi-common-lisp; Base: 10; Package: org.datagraph.spocq.implementation; -*-
2
 
3
 (in-package :org.datagraph.spocq.implementation)
4
 
5
 (:documentation "SPOCQ lmdb extensions"
6
                 "Specialize repository, revision and transaction classes
7
  to refer to rlmdb wrappers for lmdb entities.
8
  Includes simple revisioned and temporal variants.
9
  Here just the declarations.
10
 
11
  The repository classes are arranged such that
12
  - two legacy classes exist to handle rdfcache indices, in which the term order varies.
13
    these also rely on rdfcache mechanism to create storage together with the legacy web ui.
14
    the carry the protocol class RDFCACHE-REPOSITORY
15
  - abstract class relations (uniform-key x (synchronic + diachronic)) to distinguish
16
    pattern match and revision index processing variants
17
  - quad, temporal, time-series repository variants to distinguish index complements.
18
  - non rdfcache-repository concrete classes which manage storage with lmdb directly.
19
    this includes
20
    - read paths (count, match, and scan)
21
    - write paths (insert and delete statements)
22
    term interning is still performed by dydra via a read transaction on the respective
23
    account/system repository.")
24
 
25
 (:documentation "temporal repositories"
26
  "A temporal repository indexes statements according to some temporal property.
27
  This can be intrinsic, in the case of an object term which denote a dataTime or
28
  year, or it can be contingent, in the case of the respective revision ordinal
29
  for each set of statements in a timeseries of sensor samples.
30
  This requires indices in addition to the multi-dimensional spog variations.
31
  If the repository is a TEMPORAL-OBJECT-REPOSITORY repository, it indexes
32
  statements with temporal values as object terms according to the respective
33
  timeline location.
34
  If the repository is a TIME-SERIES-REPOSITORY, it combines indices which
35
  store augments the quads in each transaction with the respective revision
36
  ordinal
37
  These indices are maintained as a side-effect of repository update operations.
38
 
39
  The abstract repository classes are each specialized as an LMDB-* counterpart,
40
  which binds the respective rlmdb:repository specialization.
41
  This composes minxin classes which, in turn, comprise the index databases.
42
  For each of those, the respective -TRANSACTION and -REVISION claases are
43
  defined to act as protocol discriminators.
44
 
45
  The quad repository base supports both single- and mutliple-revision storage.
46
  For simple versioned repositories, the revision ordinals are arranges in a
47
  linear index, which is stored as the index record.
48
  For replicable repositories a sorted revision identifer sequence is stored and
49
  propagated among peers.
50
 
51
  The time-series variants stored a the revision identifier in the key.
52
  The TIME-SERIES-REPOSITORY, as the simple form, uses just the ordinal, stored
53
  as if a term id, which permits more than 4G samples.
54
  It would be possible to extend that to 64 bits or to use the a persistence
55
  revision identifer, which as a v1 uuid would provide the temporal order
56
  and also serve as a global, universal identifier for each operation.
57
  That would offer the benefit of making the time-series index replicable.
58
  Where the high bit of the time component is used as a flag to indicate
59
  insertion (= 0) and deletion (= 1) in a manner similar to its role for
60
  replication, the representation would add a slight cost of additional filtering
61
  when scanning.
62
  This would be a REPLICABLE-TIME-SERIES-REPOSITORY.
63
 
64
  wrt riak and crdt behaviour, see: https://aphyr.com/posts/285-jepsen-riak
65
 ")
66
 
67
 
68
 (defclass lmdb-transaction (shard-transaction)
69
   ((lmdb-transaction
70
     :writer setf-transaction-lmdb-transaction
71
     :reader transaction-lmdb-transaction
72
     :documentation "For a read-only transaction, bind a global
73
     lmdb-transaction to be shared among readers. For read-write transactions,
74
     this is left unbound")
75
    (max-revision-ordinal
76
     :initform (error "max-revision-ordinal is required")
77
     :initarg :max-revision-ordinal
78
     :accessor transaction-max-revision-ordinal)
79
    (min-revision-ordinal
80
     :initform (error "min-revision-ordinal is required")
81
     :initarg :min-revision-ordinal
82
     :accessor transaction-min-revision-ordinal)
83
    (max-revision-record
84
     :initform nil
85
     :reader get-transaction-max-revision-record
86
     :writer setf-transaction-max-revision-record)
87
    (min-revision-record
88
     :initform nil
89
     :reader get-transaction-min-revision-record
90
     :writer setf-transaction-min-revision-record)
91
    (highest-revision-ordinal
92
     :initform nil
93
     :reader get-transaction-highest-revision-ordinal
94
     :writer setf-transaction-highest-revision-ordinal))
95
   (:documentation "An LMDB transaction extends the base rdfcache transaction
96
    with fields for the revision bounds. The minimum values are that for
97
    the respective base revision. For a single-revision task, the maximum is the
98
    same as the minimum while, for a window, it designates the last revision.
99
    The window interval relates to the max in that it is either derived from it
100
    or determines it, depending on which is specified initially.
101
    Where a repeat interval is specifiedm the window and repeat values are used
102
    to recompute the min/max bounds.
103
    It specializes call-with-open-transaction to create and begin an lmdb
104
    transaction for that dynamic extent."))
105
 
106
 (defmethod transaction-inserted-count ((transaction lmdb-transaction))
107
   (transaction-inserted-count (transaction-lmdb-transaction transaction)))
108
 
109
 (defmethod transaction-deleted-count ((transaction lmdb-transaction))
110
   (transaction-deleted-count (transaction-lmdb-transaction transaction)))
111
 
112
 (defgeneric transaction-max-revision-record (transaction)
113
   (:method ((transaction lmdb-transaction))
114
     (or (get-transaction-max-revision-record transaction)
115
         (setf-transaction-max-revision-record (rlmdb:get-revision-record transaction (transaction-max-revision-ordinal transaction))
116
                                               transaction)))
117
   (:method ((transaction t))
118
     0))
119
 
120
 (defgeneric transaction-min-revision-record (transaction)
121
   (:method ((transaction lmdb-transaction))
122
     (or (get-transaction-min-revision-record transaction)
123
         (setf-transaction-min-revision-record (rlmdb:get-revision-record transaction (transaction-min-revision-ordinal transaction))
124
                                               transaction)))
125
   (:method ((transaction t))
126
     0))
127
 
128
 (defgeneric transaction-highest-revision-ordinal (transaction)
129
   (:method ((transaction lmdb-transaction))
130
     (or (get-transaction-highest-revision-ordinal transaction)
131
         (setf-transaction-highest-revision-ordinal (rlmdb:find-last-ordinal (transaction-repository transaction))
132
                                                    transaction)))
133
   (:method ((transaction t))
134
     0))
135
 
136
 (defgeneric transaction-next-ordinal (transaction)
137
   (:method ((transaction lmdb-transaction))
138
     (1+ (transaction-highest-revision-ordinal transaction))))
139
 
140
 (defclass lmdb-quad-transaction (lmdb-transaction quad-transaction) ())
141
 (defclass lmdb-revisioned-transaction (lmdb-transaction revisioned-transaction) ())
142
 (defclass lmdb-temporal-transaction (lmdb-transaction temporal-transaction) ())
143
 (defclass lmdb-bitemporal-transaction (lmdb-transaction bitemporal-transaction) ())
144
 (defclass lmdb-time-series-transaction (lmdb-transaction time-series-transaction) ())
145
 (defclass lmdb-bitemporal-time-series-transaction (lmdb-transaction bitemporal-time-series-transaction) ())
146
 (defclass lmdb-replicable-transaction (lmdb-transaction revisioned-transaction)
147
   ()
148
   (:documentation "An LMDB replicable transaction extends the base LMDB transaction."))
149
 
150
 (defmethod repository-lmdb-repository ((transaction lmdb-transaction))
151
   "specialize the operator for the abstract lmdb transaction class even though
152
   the slot does not appear until rdfcache-transaction is mixed in.
153
   (alternative: add transaction class to the abstract lmdb-transaction class)"
154
   (repository-lmdb-repository (transaction-revision transaction)))
155
 
156
 ;;; lmdb storage class
157
 
158
 (defclass lmdb-repository (shard-repository file-system-repository)
159
   ((lmdb-repository
160
     :initform nil
161
     :reader repository-lmdb-repository
162
     :writer setf-repository-lmdb-repository
163
     :type (or null rlmdb:repository)))
164
   (:metaclass persistent-class)
165
   (:documentation "An LMDB repository resolves its patterns against the LMDB
166
    indices, instantiates a cursor for each pattern, iterates over
167
    the visible results and yields the generated solution to a destination
168
    stream. The implementation follows that of the rdfcache id-based streaming
169
    logic, but replaces the DYDRA-NDK API with LMDB."))
170
 
171
 ;;; concrete classes which specialize access - either individually or in combination
172
 ;;; with the implementation delegated to corresponding lmdb storage classes:
173
 
174
 ;;; lmdb-direct classes
175
 
176
 (defclass lmdb-quad-repository (quad-repository lmdb-repository)
177
   ((storage-class
178
     :initform 'rlmdb:quad-repository :allocation :class)
179
    (transaction-class
180
     :initform 'lmdb-quad-transaction :allocation :class)
181
    (revision-class
182
     :initform 'lmdb-quad-revision)))
183
 
184
 (defclass lmdb-revisioned-repository (quad-repository lmdb-repository revision-metadata-repository)
185
   ((storage-class
186
     :initform 'rlmdb:revisioned-repository :allocation :class)
187
    (transaction-class
188
     :initform 'lmdb-revisioned-transaction :allocation :class)
189
    (revision-class
190
     :initform 'lmdb-revisioned-revision)))
191
 
192
 (defclass lmdb-temporal-repository (temporal-repository lmdb-repository quad-repository)
193
   ((storage-class
194
     :initform 'rlmdb:temporal-repository :allocation :class)
195
    (transaction-class
196
     :initform 'lmdb-temporal-transaction :allocation :class)
197
    (revision-class
198
     :initform 'lmdb-temporal-revision)))
199
 
200
 (defmethod repository-temporal-predicate-p ((repository lmdb-temporal-repository) (predicate t))
201
   (rlmdb:repository-temporal-predicate-p (repository-lmdb-repository repository) predicate))
202
 
203
 
204
 (defclass lmdb-bitemporal-repository (temporal-repository lmdb-repository quad-repository revision-metadata-repository)
205
   ((storage-class
206
     :initform 'rlmdb:bitemporal-repository :allocation :class)
207
    (transaction-class
208
     :initform 'lmdb-bitemporal-transaction :allocation :class)
209
    (revision-class
210
     :initform 'lmdb-bitemporal-revision)))
211
 
212
 ;; 20200924: abstract only: rdfcache always adds the quad indices
213
 (defclass lmdb-time-series-repository (time-series-repository lmdb-repository)
214
   ((transaction-class
215
     :initform 'lmdb-time-series-transaction)
216
    (revision-class
217
     :initform 'lmdb-time-series-revision))
218
   (:documentation "Combine the revision uuid/ordinal/timestamp metadata"))
219
 
220
 (defclass lmdb-time-series-quad-repository (lmdb-time-series-repository quad-repository revision-metadata-repository)
221
   ((storage-class
222
     :initform 'rlmdb:time-series-quad-repository :allocation :class))
223
   (:documentation "Combine the revision uuid/ordinal/timestamp metadata with the ordinal index"))
224
 
225
 (defclass lmdb-time-series-temporal-repository (lmdb-time-series-repository temporal-repository revision-metadata-repository)
226
   ((storage-class
227
     :initform 'rlmdb:time-series-temporal-repository :allocation :class))
228
   (:documentation "Combine the revision uuid/ordinal/timestamp metadata with the temporal index"))
229
 
230
 (defclass lmdb-time-series-bitemporal-repository (lmdb-time-series-repository bitemporal-repository)
231
   ((storage-class
232
     :initform 'rlmdb:time-series-bitemporal-repository :allocation :class)))
233
 
234
 (defmethod repository-time-series-predicate-p ((repository lmdb-time-series-repository) (predicate t))
235
   (rlmdb:repository-time-series-predicate-p (repository-lmdb-repository repository) predicate))
236
 
237
 (defmethod repository-time-series-predicates ((repository lmdb-time-series-repository))
238
   (rlmdb:repository-time-series-predicates (repository-lmdb-repository repository)))
239
 
240
 (defmethod repository-time-series-predicate-ids ((repository lmdb-time-series-repository))
241
   (rlmdb.i::repository-time-series-predicate-ids (repository-lmdb-repository repository)))
242
 
243
 
244
 (defclass lmdb-replicable-repository (replicable-repository lmdb-revisioned-repository)
245
   ((creation-timestamp :initform (spocq.e::unix-now)
246
                        :reader repository-creation-timestamp)
247
    (storage-class
248
     :initform 'rlmdb:replicable-repository :allocation :class)
249
    (transaction-class
250
     :initform 'lmdb-replicable-transaction :allocation :class)
251
    (revision-class 
252
     :initform 'lmdb-replicable-revision :allocation :class))
253
   (:documentation "An LMDB replicable repository behaves as an LMDB repository, but records
254
    visibilty as revision identifiers, rather then ordinals."))
255
 
256
 
257
 
258
 (defclass lmdb-revision (lmdb-repository) 
259
   ((min-revision-ordinal
260
     :initform (error "min-revision-ordinal is required")
261
     :initarg :min-revision-ordinal
262
     :accessor revision-min-revision-ordinal)
263
    (max-revision-ordinal
264
     :initform (error "max-revision-ordinal is required")
265
     :initarg :max-revision-ordinal
266
     :accessor revision-max-revision-ordinal)
267
    (min-revision-record
268
     :initform (error "min-revision-record is required")
269
     :initarg :min-revision-record
270
     :accessor revision-min-revision-record)
271
    (max-revision-record
272
     :initform (error "max-revision-record is required")
273
     :initarg :max-revision-record
274
     :accessor revision-max-revision-record)
275
    (window-interval
276
     :initform nil :initarg :window-interval
277
     :accessor revision-window-interval)
278
    (repeat-interval
279
     :initform nil :initarg :repeat-interval
280
     :accessor revision-repeat-interval)
281
    (repeat-limit
282
     :initform 0 :initarg :repeat-limit
283
     :accessor revision-repeat-limit
284
     :documentation "Taken together with the repeat interval, this governs how often
285
     and for which bounds the revision is to be applied.")
286
    (relation
287
     :initform nil :initarg :relation
288
     :accessor revision-relation
289
     :documentation "specifies a logical combination of intervals from allen's algebra
290
     to constrain the match results to those which satisfy the interval constraint."))
291
   (:documentation "Extend the basic revision definition with fields for window,
292
    repeat intervals, and relation."))
293
 
294
 (defclass lmdb-quad-revision (lmdb-revision quad-revision lmdb-quad-repository) ())
295
 (defclass lmdb-revisioned-revision (lmdb-revision revisioned-revision lmdb-revisioned-repository) ())
296
 (defclass lmdb-temporal-revision (lmdb-revision temporal-revision lmdb-temporal-repository) ())
297
 (defclass lmdb-bitemporal-revision (lmdb-revision bitemporal-revision lmdb-bitemporal-repository) ())
298
 (defclass lmdb-time-series-revision (lmdb-revision time-series-revision lmdb-time-series-repository) ())
299
 (defclass lmdb-bitemporal-time-series-revision (lmdb-revision bitemporal-time-series-revision lmdb-bitemporal-time-series-repository) ())
300
 (defclass lmdb-replicable-revision (lmdb-revision lmdb-replicable-repository)
301
   ((transaction-class
302
     :initform 'lmdb-replicable-transaction :allocation :class)))
303
 
304
 ;;; rdfcache-based legacy classes for repositories keyed with varied term order
305
 ;;; these DO NOT distinguish mono- v/s multi- version storage,
306
 ;;; but instead just extend the base class with revisioned capabilities
307
 
308
 (defgeneric revision-relation (revision)
309
   (:method ((repository repository))
310
     nil))
311
 
312
 (defclass rdfcache-lmdb-transaction (rdfcache-transaction lmdb-transaction quad-transaction)
313
   ())
314
 
315
 (defclass lmdb-rdfcache-transaction (lmdb-transaction rdfcache-transaction quad-transaction)
316
   ())
317
 
318
 (defclass rdfcache-lmdb-repository (rdfcache-repository quad-repository lmdb-repository)
319
   ((storage-class
320
     :initform 'rlmdb:rdfcache-quad-repository :allocation :class)
321
    (transaction-class
322
      ;; :initform 'lmdb-rdfcache-transaction ;; this needs implementation for insertion/deletion
323
      :initform 'rdfcache-lmdb-transaction
324
     :allocation :instance)
325
    (revision-class
326
     :allocation :class
327
     :initform 'rdfcache-lmdb-repository-revision))
328
   (:metaclass persistent-class)
329
   (:documentation "The lmdb-repository class manages legacy rdfcache repositories"))
330
 
331
 (defclass revisioned-rdfcache-lmdb-repository (revisioned-repository rdfcache-lmdb-repository)
332
   ((storage-class
333
     :initform 'rlmdb:revisioned-rdfcache-quad-repository :allocation :class))
334
   (:metaclass persistent-class)
335
   (:documentation "The revisioned-lmdb-repository class manages legacy rdfcache repositories"))
336
 
337
 
338
 (defclass rdfcache-lmdb-repository-revision (lmdb-revision rdfcache-lmdb-repository rdfcache-stream-repository-revision)
339
   ())
340
 
341
 (defclass rdfcache-lmdb-revisioned-repository-revision (lmdb-revision revisioned-rdfcache-lmdb-repository rdfcache-stream-repository-revision)
342
   ())
343
 
344
 
345
 (pushnew (list *class.revisioned-repository* (cl-ppcre:create-scanner "[^/]+/.+__(rev|arc(hive)?)"))
346
          *repository-id-type-map*
347
          :key #'first)
348
 ;;; (mapcar #'repository-id-type '("asdf/qwer" "asdf/qwer__archive" "asdf/qwer__arc"))
349
 
350
 (defgeneric repository-revision-ordinal-size (repository)
351
   (:method ((repository lmdb-repository)) 4)
352
   (:method ((transaction lmdb-transaction)) (repository-revision-ordinal-size (transaction-repository transaction))))
353
 
354
 (defmethod repository-storage ((repository lmdb-repository))
355
   (repository-lmdb-repository repository))
356
 
357
 ;; repository predicates which depend on lmdb locations and implmentation
358
 
359
 (defmethod replicable-repository-p ((location pathname))
360
   (when (setf location (probe-file (merge-pathnames *lmdb-filename* location)))
361
     (replicable-repository-p (rlmdb:get-metadata location))))
362
 
363
 (defmethod replicable-repository-p ((metadata list))
364
   (declare (special rlmdb.i::*revision-ordinal-database-name*))
365
   (find rlmdb.i::*revision-sequence-database-name*
366
         (rest (assoc :database-names metadata :test #'string-equal))
367
         :test #'equal))
368
 
369
 (defmethod revisioned-repository-p ((location pathname))
370
   (when (setf location (probe-file (merge-pathnames *lmdb-filename* location)))
371
     (revisioned-repository-p (rlmdb:get-metadata location))))
372
 
373
 (defmethod revisioned-repository-p ((metadata list))
374
   (declare (special rlmdb.i::*revision-ordinal-database-name*))
375
   (find rlmdb.i::*revision-ordinal-database-name*
376
         (rest (assoc :database-names metadata :test #'string-equal))
377
         :test #'equal))
378
 
379