Coverage report: /development/source/library/org/datagraph/spocq-shard/src/store/rlmdb/replicable-database.lisp

KindCoveredAll%
expression01998 0.0
branch0170 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.rlmdb.implementation; -*-
2
 
3
 (in-package :rlmdb.i)
4
 ;;; (load "patches/replicable-database.lisp")
5
 ;;; (load "patches/revision-identifier.lisp")
6
 
7
 (:documentation "replicable databases"
8
                 "A replicable database extends the rlmdb:repository with a specialized rlmdb:database
9
  which store revision sequence vectors keyed by their hash codes and supports revision presence tests
10
  against quad presence vectors which are sequence vector hash codes.
11
 
12
  * classes
13
  - rlmdb:replicable-repository : add a revision-sequence-db
14
  - rlmdb:replicable-database
15
  - rlmdb::replicable-quad-database
16
  - rlmdb:revision-sequence-database : stores revision sequences by hash code.
17
 ")
18
 
19
 (cffi:defcstruct sha1
20
   (bytes :uint8 :count 20))
21
 
22
 
23
 (defun %decode-revision-sequence-id (%data &optional (result (make-array spocq.i::+rsid-length+ :element-type '(unsigned-byte 8))))
24
   "Extract a sequence of uuid vectors from foreign data"
25
   (loop for i below spocq.i::+rsid-length+
26
     do (setf (aref result i) (cffi:mem-aref %data :uint8 i)))
27
   result)
28
 
29
 (defun %encode-revision-sequence-id (%data revision-sequence-id)
30
   "Extract a sequence of uuid vectors from foreign data"
31
   (loop for i below spocq.i::+rsid-length+
32
     do (setf (cffi:mem-aref %data :uint8 i) (aref revision-sequence-id i)))
33
   %data)
34
 
35
 (defun %decode-revision-sequence (%data length)
36
   "Extract a sequence of uuid vectors from foreign data"
37
   (let* ((count (/ length spocq.i::+uuid-length+))
38
          (result (make-array count)))
39
     (loop for i below count
40
       with offset = 0
41
       for uuid = (spocq.i::make-uuid-vector)
42
       do (progn (setf (aref result i) uuid)
43
            (loop for j below spocq.i::+uuid-length+
44
              do (setf (aref uuid j) (cffi:mem-aref %data :uint8 offset)
45
                       offset (1+ offset)))))
46
     result))
47
 
48
 (defun %encode-revision-sequence (%data revision-sequence)
49
   "Constract a packed foreign sequence from a revision uuid sequence."
50
   (let* ((count (length revision-sequence)))
51
     (loop for i below count
52
       with offset = 0
53
       for uuid = (aref revision-sequence i)
54
       do (loop for j below spocq.i::+uuid-length+
55
            do (setf (cffi:mem-aref %data :uint8 offset) (aref uuid j)
56
                     offset (1+ offset)))))
57
   %data)
58
 
59
 (defun get-metadata-uuid-vector (designator)
60
   (spocq.i::copy-uuid-vector (rlmdb:get-metadata-uuid designator)))
61
 
62
 (defstruct (rlmdb::revision-application-log-record (:include rlmdb:revision-log-record))
63
   "An application log record adds the uuid from the replica/application in order to place the transaction bi-temporally"
64
   (application-uuid +null-uuid-string+ :type string))
65
 
66
 ;;; a revision application log record augments the standard revision information with the replica uuid
67
 ;;; this combines that revision with the local revision into a 2-d revision location.
68
 (cffi:defcstruct revision_application_log_record
69
   (version :uint8)
70
   (flags :uint8)
71
   (transaction_uuid :unsigned-char :count 16)
72
   (timestamp_begun :uint64 :offset 18)
73
   (timestamp_committed :uint64 :offset 26)
74
   (visible_count :uint64 :offset 34)
75
   (removed_count :uint64 :offset 42)
76
   (inserted_count :uint64 :offset 50)
77
   (application_uuid :unsigned-char :count 16))
78
 ;;; (+ 1 1 16 8 8 8 8 8 16)
79
 
80
 (defparameter +application_log_record-size+ 74)
81
 
82
 (defun %decode-revision-application-log-record (record)
83
   (make-revision-application-log-record
84
    :version (cffi:foreign-slot-value record '(:struct revision_application_log_record) 'version)
85
    :flags (cffi:foreign-slot-value record '(:struct revision_application_log_record) 'flags)
86
    :uuid (rlmdb:decode-metadata :|revision-uuid| (cffi:foreign-slot-value record '(:struct revision_application_log_record) 'transaction_uuid))
87
    :timestamp (cffi:foreign-slot-value record '(:struct revision_application_log_record) 'timestamp_committed)
88
    :timestamp-begun (cffi:foreign-slot-value record '(:struct revision_application_log_record) 'timestamp_begun)
89
    ;; nb. the visible count is not maintained by the rdfcache implementation
90
    :visible-count (cffi:foreign-slot-value record '(:struct revision_application_log_record) 'visible_count)
91
    :removed-count (cffi:foreign-slot-value record '(:struct revision_application_log_record) 'removed_count)
92
    :inserted-count (cffi:foreign-slot-value record '(:struct revision_application_log_record) 'inserted_count)
93
    :uuid (rlmdb:decode-metadata :|revision-uuid| (cffi:foreign-slot-value record '(:struct revision_application_log_record) 'application_uuid))))
94
 
95
 (defgeneric %encode-revision-application-log-record (record %record)
96
   (:method ((record rlmdb::revision-application-log-record) (%record SB-SYS:SYSTEM-AREA-POINTER))
97
     (cffi:with-foreign-slots ((version flags
98
                                timestamp_committed timestamp_begun
99
                                visible_count removed_count inserted_count)
100
                               %record
101
                               (:struct revision_application_log_record))
102
       (spocq.i::%encode-uuid (rlmdb:revision-log-record-uuid record)
103
                              (cffi:foreign-slot-pointer %record '(:struct revision_application_log_record) 'transaction_uuid))
104
       (setf version (rlmdb:revision-log-record-version record)
105
             flags (rlmdb:revision-log-record-flags record)
106
             timestamp_committed (rlmdb:revision-log-record-timestamp record)
107
             timestamp_begun (rlmdb:revision-log-record-timestamp-begun record)
108
             visible_count (rlmdb:revision-log-record-visible-count record)
109
             removed_count (rlmdb:revision-log-record-removed-count record)
110
             inserted_count (rlmdb:revision-log-record-inserted-count record))
111
       (spocq.i::%encode-uuid (revision-application-log-record-application-uuid record)
112
                              (cffi:foreign-slot-pointer %record '(:struct revision_application_log_record) 'application_uuid))
113
       %record)))
114
 
115
 ;;; (with-lmdb-values ((key 1 2) (data 3 4)) (list key data))
116
 
117
 
118
 
119
 
120
 
121
 
122
 (defmethod rlmdb:clear-repository max ((repository rlmdb:replicable-repository) &key (type 'nil))
123
   (let ((count nil))
124
     (flet ((clear-db (db)
125
              (when (typep db type)
126
                (unless count (setf count (rlmdb::entry-count db)))
127
                (lmdb:drop-database db :delete 0))))
128
       (clear-db (repository-revision-sequence-database repository)))
129
     (or count 0)))
130
 
131
 ;;;
132
 
133
 (defgeneric repository-revision-sequence (repository sequence-id)
134
   (:documentation "retrieve and decode the identified revision sequence.")
135
   (:method ((repository rlmdb:replicable-repository) (sequence-id vector))
136
     "given a vector sha1 value extract the native pointer and continue"
137
     (cffi:with-foreign-pointer (%sequence-id spocq.i::+rsid-length+)
138
       (%encode-revision-sequence-id %sequence-id sequence-id)
139
       (repository-revision-sequence repository %sequence-id)))
140
   (:method ((repository rlmdb:replicable-repository) (%sequence-id SB-SYS:SYSTEM-AREA-POINTER))
141
     "given an external sha1 value, retrieve the vector, if known and return the boxed sequence"
142
     (lmdb::with-empty-value (%key)
143
       (lmdb::with-empty-value (%value)
144
         (setf (%mdb-val-size %key) spocq.i::+rsid-length+
145
               (%mdb-val-data %key) %sequence-id)
146
         (let* ((db (repository-revision-sequence-database repository))
147
                (return-code (liblmdb:get (lmdb::handle lmdb:*transaction*) (lmdb::handle db) %key %value)))
148
           (cond ((zerop return-code)
149
                  (%decode-revision-sequence (%mdb-val-data %value) (%mdb-val-size %value)))
150
                 ((eql return-code liblmdb:+notfound+)
151
                  (error "no state-vector found: ~a" %sequence-id))
152
                 (t
153
                  (lmdb::unknown-error return-code))))))))
154
 
155
 
156
 
157
 
158
 (defmethod rlmdb:map-repository-statements (operator (index rlmdb::replicable-quad-database) (quad-pattern t)
159
                                                         &key (first nil) (last nil) (mode 'every)
160
                                                         revision-predicate domain-predicate
161
                                                         scan-order)
162
   "Use the pattern to locate and constrain a scan over the statements' index values.
163
 The abstract process is the same as for an isolated repository.
164
 In the replicated case, however, each index entry is used, first to check in a transaction cache for a visibility determination.
165
 the cache key is the sequence sha1, as the index entry does not de-duplicate sequences.
166
 if a cache entry is present, that determines the respective statement visibilty.
167
 if no cache entry is present, the actual sha1 hash is used to retrieve the complete revision sequence.
168
 this is used to test visibility given the transaction revision - latest or temporally significant.
169
 the eventual determination is then cached per sequence sha1.
170
 "
171
   (declare (ignore predicates revision-predicate temporal-predicate scan-order))
172
   (unless first
173
     (setf first (rlmdb:find-last-ordinal index)))
174
   (if last
175
       (setf first (spocq.i::copy-uuid-vector (rlmdb:get-revision-uuid index first))
176
             last (spocq.i::copy-uuid-vector (rlmdb:get-revision-uuid index last)))
177
       (if first
178
           (setf first (spocq.i::copy-uuid-vector (rlmdb:get-revision-uuid index first))
179
                 last first)))
180
   
181
   (lmdb:with-database (index)
182
     (let* ((cur (lmdb:make-cursor index :transaction lmdb:*transaction*))
183
            (named-only (case (graph quad-pattern)
184
                          ((-2 |urn:dydra|:|named|) t)
185
                          (t nil)))
186
            (quad-graph-index 0)
187
            (wild-pattern-p (wild-quad-pattern-p quad-pattern))
188
            (visibility-cache (make-hash-table :test 'equalp))
189
            (test-rsid (make-array spocq.i::+rsid-length+ :element-type 'unsigned-byte))
190
            (default-graph-term-id (rlmdb:transaction-default-context-term-id lmdb:*transaction*)))
191
       ;;(let ((%key-quad (cffi:foreign-alloc '(:struct rdfcache::quad))))
192
       (cffi:with-foreign-objects ((%quad-pattern '(:struct rdfcache::quad))
193
                                   (%key-quad '(:struct rdfcache::quad)))
194
         (lmdb::with-empty-value (%key)
195
           (lmdb::with-empty-value (%value)
196
             (flet ((map-for-graph (quad-pattern)
197
                      ;;(print :let)
198
                      (incf spocq.i::*match-requests*)
199
                      (quad-to-quad-record quad-pattern %quad-pattern)
200
                      (%copy-quad %quad-pattern %key-quad)
201
                      ;;(print :initial)
202
                      ;;(%print-quad %quad-pattern *trace-output*)
203
                      ;;(%print-quad %key-quad *trace-output*)
204
                      (lmdb:with-cursor (cur)
205
                        (let ((%cursor (lmdb::handle cur)))
206
                        (labels ((get-quad (get-op)
207
                                   ;(lmdb::with-empty-value (%key)
208
                                   ;(lmdb::with-empty-value (%value)
209
                                   (ecase get-op
210
                                     (:+set-range+
211
                                      (setf (%mdb-val-size %key) 16
212
                                            (%mdb-val-data %key) %key-quad)
213
                                      ;; (%print-quad %key-quad *trace-output*)
214
                                      )
215
                                     (:+next+ ))
216
                                   ;;(print :key-quad *trace-output*)
217
                                   ;;(%print-quad %key-quad *trace-output*)
218
                                   (let ((return-code (liblmdb:cursor-get %cursor %key %value get-op)))
219
                                     ;; (print (list quad-pattern return-code))
220
                                     (alexandria:switch (return-code)
221
                                                        (0
222
                                                         (call-with-quad-entry %key %value))
223
                                                        (liblmdb:+notfound+
224
                                                         nil)
225
                                                        (t
226
                                                         (lmdb::unknown-error return-code)))))
227
                                 (call-with-quad-entry (%k %v)
228
                                   ;;(print :call-with-quad-entry)
229
                                   (assert (= 16 (cffi:pointer-address (cffi:foreign-slot-value %k '(:struct liblmdb:val) 'liblmdb:mv-size)))
230
                                           ()
231
                                           "key size is invalid: ~s" (cffi:pointer-address (cffi:foreign-slot-value %k '(:struct liblmdb:val) 'liblmdb:mv-size)))
232
                                   (let* ((%index-quad (%mdb-val-data %k))
233
                                          (%index-sha1 (%mdb-val-data %v))
234
                                          ;; (index-sha1-addr (cffi:pointer-address %index-sha1))
235
                                          )
236
                                     ;; continue until either no longer matched or the operator returns nil
237
                                     ;; (print (list :no named-only :qm quad-map :qgi quad-graph-index :g (cffi:mem-aref %index-quad 'term-id quad-graph-index)))
238
                                     ;; (%print-quad %index-quad *trace-output*)
239
                                     #+(or)
240
                                     (print (list :rms named-only
241
                                                  (= (cffi:mem-aref %index-quad 'term-id quad-graph-index) default-graph-term-id)
242
                                                  wild-pattern-p
243
                                                  (%quad-match-p %quad-pattern %index-quad)))
244
                                     (cond ((and named-only (= (cffi:mem-aref %index-quad 'term-id quad-graph-index) default-graph-term-id))
245
                                            ;; skip
246
                                            t)
247
                                           ((or wild-pattern-p (%quad-match-p %quad-pattern %index-quad))
248
                                            ;; iff still in range,
249
                                            (cond (first
250
                                                   ;; recall or derive visibility
251
                                                   (%decode-revision-sequence-id %index-sha1 test-rsid)
252
                                                   #+(or) (print (list :rsid test-rsid))
253
                                                   (multiple-value-bind (visible known) (gethash test-rsid visibility-cache)
254
                                                     (if (if known
255
                                                             visible
256
                                                             (let* ((revision-sequence (replica-get-revision-sequence (database-repository index)
257
                                                                                                                      %index-sha1))
258
                                                                    (visibility (spocq.i::test-uuid-range-visibility first last revision-sequence :mode mode)))
259
                                                               (setf (gethash (copy-seq test-rsid) visibility-cache) visibility)))
260
                                                         ;; if visible apply op and return the yes/no continue indication
261
                                                         (map-repository-statements-callback operator %index-quad)
262
                                                         ;; otherwise skip
263
                                                         t)))
264
                                                ;; without constraints return the raw values
265
                                                (t (funcall operator %index-quad %index-sha1))))
266
                                           ;; otherwise end
267
                                           (t nil)))))
268
                      (loop for op = :+set-range+ then :+next+
269
                        with count = 0
270
                        while (get-quad op)
271
                        do (incf count)
272
                        finally (return count)))))))
273
           (typecase (graph quad-pattern)
274
             (cons (loop with single-graph-quad-pattern = (copy-quad-pattern quad-pattern)
275
                     for graph in (graph quad-pattern)
276
                     do (progn (setf (graph single-graph-quad-pattern) graph))
277
                     sum (map-for-graph single-graph-quad-pattern)))
278
             (t (when (eql (graph quad-pattern) -2)
279
                  (setf (graph quad-pattern) 0))
280
                (map-for-graph quad-pattern))))))))))
281
 
282
 
283
 (defmethod rlmdb:map-repository-statements-filtered (operator (index rlmdb::replicable-quad-database) (quad-pattern t) filter)
284
   "Map across statements according to logic which combined the replication visibility logic with the interval relation logic.
285
 
286
 In the replicated case, however, each index entry is used, first to check in an expression-related cache for a visibility determination.
287
 the cache key is the sequence sha1, as the index entry does not de-duplicate sequences.
288
 if a cache entry is present, that determines the respective statement visibilty.
289
 if no cache entry is present, the actual sha1 hash is decoded and used to retrieve the complete revision sequence.
290
 this is used to test visibility given the transaction revision - latest or temporally significant.
291
 the eventual determination is then cached per sequence sha1 and then determines visibility.
292
 "
293
 
294
   (lmdb:with-database (index)
295
     (let* ((cur (lmdb:make-cursor index :transaction lmdb:*transaction*))
296
            (named-only (case (graph quad-pattern)
297
                          ((-2 |urn:dydra|:|named|) t)
298
                          (t nil)))
299
            (quad-graph-index 0)
300
            (wild-pattern-p (wild-quad-pattern-p quad-pattern))
301
            (visibility-cache (make-hash-table :test 'equalp))
302
            (test-rsid (make-array spocq.i::+rsid-length+ :element-type 'unsigned-byte))
303
            (match-count 0)
304
            (scan-count 0)
305
            (default-graph-term-id (rlmdb:transaction-default-context-term-id lmdb:*transaction*)))
306
       ;;(let ((%key-quad (cffi:foreign-alloc '(:struct rdfcache::quad))))
307
       (cffi:with-foreign-objects ((%quad-pattern '(:struct rdfcache::quad))
308
                                   (%key-quad '(:struct rdfcache::quad)))
309
         (lmdb::with-empty-value (%key)
310
           (lmdb::with-empty-value (%value)
311
             (flet ((map-for-graph (quad-pattern)
312
                      ;;(print :let)
313
                      (incf spocq.i::*match-requests*)
314
                      (quad-to-quad-record quad-pattern %quad-pattern)
315
                      (%copy-quad %quad-pattern %key-quad)
316
                      ;;(print :initial)
317
                      ;;(%print-quad %quad-pattern *trace-output*)
318
                      ;;(%print-quad %key-quad *trace-output*)
319
                      (lmdb:with-cursor (cur)
320
                        (let ((%cursor (lmdb::handle cur)))
321
                        (labels ((get-quad (get-op)
322
                                   ;(lmdb::with-empty-value (%key)
323
                                   ;(lmdb::with-empty-value (%value)
324
                                   (ecase get-op
325
                                     (:+set-range+
326
                                      (setf (%mdb-val-size %key) 16
327
                                            (%mdb-val-data %key) %key-quad)
328
                                      ;; (%print-quad %key-quad *trace-output*)
329
                                      )
330
                                     (:+next+ ))
331
                                   ;;(print :key-quad *trace-output*)
332
                                   ;;(%print-quad %key-quad *trace-output*)
333
                                   (let ((return-code (liblmdb:cursor-get %cursor %key %value get-op)))
334
                                     ;; (print (list quad-pattern return-code))
335
                                     (alexandria:switch (return-code)
336
                                                        (0
337
                                                         (incf scan-count)
338
                                                         (call-with-quad-entry %key %value))
339
                                                        (liblmdb:+notfound+
340
                                                         nil)
341
                                                        (t
342
                                                         (lmdb::unknown-error return-code)))))
343
                                 (call-with-quad-entry (%k %v)
344
                                   ;;(print :call-with-quad-entry)
345
                                   (assert (= 16 (%mdb-val-size %k))
346
                                           ()
347
                                           "key size is invalid: ~s" (%mdb-val-size %k))
348
                                   (let* ((%index-quad (%mdb-val-data %k))
349
                                          (%index-sha1 (%mdb-val-data %v))
350
                                          (visibility-bytes (%mdb-val-size %v))
351
                                          ;; (index-sha1-addr (cffi:pointer-address %index-sha1))
352
                                          )
353
                                     ;; continue until either no longer matched or the operator returns nil
354
                                     ;; (print (list :no named-only :qm quad-map :qgi quad-graph-index :g (cffi:mem-aref %index-quad 'term-id quad-graph-index)))
355
                                     ;; (%print-quad %index-quad *trace-output*)
356
                                     #+(or)
357
                                     (print (list :rms named-only
358
                                                  (= (cffi:mem-aref %index-quad 'term-id quad-graph-index) default-graph-term-id)
359
                                                  wild-pattern-p
360
                                                  (%quad-match-p %quad-pattern %index-quad)))
361
                                     (cond ((and named-only (= (cffi:mem-aref %index-quad 'term-id quad-graph-index) default-graph-term-id))
362
                                            ;; skip
363
                                            t)
364
                                           ((or wild-pattern-p (%quad-match-p %quad-pattern %index-quad))
365
                                            ;; iff still in range,
366
                                            ;; recall or derive visibility
367
                                            (if (zerop visibility-bytes)
368
                                                (if (map-repository-statements-filtered-callback operator %index-quad nil nil)
369
                                                    (incf match-count)
370
                                                    (return-from call-with-quad-entry nil))
371
                                                (flet ((continue-with-success (first last)
372
                                                         ;; success values augment the quad to be bound in the bgp
373
                                                         ;; continuation control is left to continue
374
                                                         (unless (map-repository-statements-filtered-callback operator %index-quad first last)
375
                                                           ;; terminate iff caller indicates to stop
376
                                                           (return-from call-with-quad-entry nil))))
377
                                                  (%decode-revision-sequence-id %index-sha1 test-rsid)
378
                                                  #+(or) (print (list :rsid test-rsid))
379
                                                  (multiple-value-bind (visibility known) (gethash test-rsid visibility-cache)
380
                                                    (unless known
381
                                                      (flet ((record-bounds (first last)
382
                                                         (setf visibility (list first last))))
383
                                                        (declare (dynamic-extent #'record-bounds))
384
                                                        (let ((version-map (replica-get-revision-sequence (database-repository index)
385
                                                                                                          %index-sha1)))
386
                                                          ;; this will need to record a list of visibilities
387
                                                          (filter-matched-statements filter #'record-bounds version-map)))
388
                                                      (setf (gethash (copy-seq test-rsid) visibility-cache) visibility))
389
                                                    (cond (visibility
390
                                                           (incf match-count)
391
                                                           ;; if visible apply op and return the yes/no continue indication
392
                                                           (apply #'continue-with-success visibility))
393
                                                          ;; otherwise skip
394
                                                          (t t))))))
395
                                           ;; otherwise end
396
                                           (t nil)))))
397
                      (loop for op = :+set-range+ then :+next+
398
                        while (get-quad op)))))))
399
           (typecase (graph quad-pattern)
400
             (cons (loop with single-graph-quad-pattern = (copy-quad-pattern quad-pattern)
401
                     for graph in (graph quad-pattern)
402
                     do (progn (setf (graph single-graph-quad-pattern) graph)
403
                          (map-for-graph single-graph-quad-pattern))))
404
             (t (when named-only
405
                  (setf (graph quad-pattern) 0))
406
                (map-for-graph quad-pattern)))))))
407
       (values scan-count match-count visibility-cache))))
408
 
409
 
410
 ;;; !!! filter form should permit revision id intervals as well as atomic values
411
 #|
412
 no longer used
413
 (defgeneric match-replicated-statement-visibility (continue revision-sequence-map relation)
414
   )
415
 
416
 (defgeneric match-replicated-revision-interval (continue map position-or-test)
417
   (:documentation "Given a visibility map and either a relation - as either an expression or a function,
418
     or a revision identifier - as a vector; determine whether the visibility satisfies the relation and,
419
     if so, invoke the continuation with the bounds.")
420
   (:method (continue map (revision-designator vector))
421
     "Given just a position, locate the immediately previous operation.
422
      If one is present and if it inserted the statement, locate any end position
423
      and yield the bounds."
424
     (let ((position-start  (position-uuid-vector revision-designator map)))
425
       (when position-start
426
         (let ((uuid-start (aref map position-start)))
427
           (when (insert-uuid-p uuid-start)
428
             (let ((position-end (revision-transition-next (1+ position-start) map :transition-p #'delete-uuid-p)))
429
               (funcall continue uuid-start (when position-end (aref map position-end)))))))))
430
   (:method (continue map (relation-test function))
431
     "Given a predicate function, apply it to the map and allow it to yield any result."
432
     (funcall relation-test continue map))
433
   (:method (continue map (relation-expression cons))
434
     "Given a test expression, apply it to the map and yield any match."
435
     (test-visibility-map continue map relation-expression)))
436
 |#
437
 
438
 ;;; (trace match-statement-visibility-disjoint statement-visibility-next-bounds statement-visibility-previous-bounds)
439
 
440
 #|
441
  each basic search functions yields one of threeintervls in relation to the given designator
442
 
443
                    previous   designator    next
444
                        |          |          |
445
  bounds-previous       [          )
446
  bounds                           [          ) 
447
  bounds-next                                 [         )
448
 
449
  where the bounds and bounds-after can yielf null for the end       
450
 |#
451
 
452
 
453
 
454
 (defun search-revision-bounds-next  (revision-designator sequence &optional (test #'insert-uuid-p))
455
   "return start and end bounds for the interval starting at a revision > the given designator
456
    iff it is accepted by the test.
457
    a beginning w/o a complementing end yields a null end."
458
   (let* ((position-<= (position-uuid-vector revision-designator sequence)))
459
     (when position-<=
460
       (let ((uuid-<= (aref sequence position-<=)))
461
         (when (not (funcall test uuid-<=))
462
           (let ((position-start (revision-transition-next (1+ position-<=) sequence :transition-p test)))
463
             (when position-start
464
               (let ((uuid-start (aref sequence position-start)))  ;; no need to retest
465
                 (let ((position-end (revision-transition-next (1+ position-start) sequence :transition-p (complement test))))
466
                   (values uuid-start (when position-end (aref sequence position-end)) position-start position-end))))))))))
467
 
468
 
469
 
470
 (defun search-revision-bounds>=  (revision-designator sequence &optional (test #'insert-uuid-p))
471
   "return start and end bounds for the interval starting at a revision >= the given designator
472
    iff it is accepted by the test.
473
    a beginning w/o a complementing end yields a null end.
474
    the default predicate locates insert-delete intervals."
475
   ;; in this case, the immediate position is itself the lower bounds
476
   (flet ((try-next-transition (start)
477
            (let ((position-> (revision-transition-next start sequence :transition-p test)))
478
              (when position->
479
                (let ((uuid-start (aref sequence position->)))  ;; no need to retest
480
                  (let ((position-end (revision-transition-next (1+ position->) sequence :transition-p (complement test))))
481
                    (values uuid-start (when position-end (aref sequence position-end)))))))))
482
     (let* ((position-<= (position-uuid-vector revision-designator sequence)))
483
       (cond (position-<= ;; some earlier transition exists
484
              (let ((uuid-start (aref sequence position-<=)))
485
                ;; if the revision at that location meets the test
486
                (if (and (revision-coincident-p uuid-start revision-designator(funcall test uuid-start))
487
                    ;; then locate its matching successor
488
                    (let ((position-end (revision-transition-next (1+ position-<=) sequence :transition-p (complement test))))
489
                      (values uuid-start (when position-end (aref sequence position-end))))
490
                    ;; otherwise, look for the next one
491
                    (try-next-transition (1+ position-<=)))))
492
             ((> (length sequence) 0) ;; nothing earlier, look for next
493
              (try-next-transition 0))))))
494
 
495
 (defun search-revision-bounds<  (revision-designator sequence &optional (test #'insert-uuid-p))
496
   "return start and end bounds for the interval ending at a revision < the given designator.
497
    the end is established by being accepted by a complement of the test.
498
    the default test locates insert-delete intervals."
499
   ;; in this case, the immediate position is itself the lower bounds
500
   (flet ((try-previous-transition (end)
501
            (let ((position< (revision-transition-previous end sequence :transition-p (complement test))))
502
              (when position<
503
                (let ((uuid-end (aref sequence position<)))  ;; no need to retest
504
                  (let ((position-start (revision-transition-previous (1- position<) sequence :transition-p test)))
505
                    (when position-start
506
                      (values (aref sequence position-start) uuid-end))))))))
507
     (let* ((position<= (position-uuid-vector revision-designator sequence)))
508
       (when position<=
509
         ;; some earlier transition exists
510
         (let ((uuid-end (aref sequence position<=)))
511
           ;; if it meets the test return the previous test/fail bounds
512
           ;; otherwise, iff it is previous, try it as end, otherwise locate its start and try before there
513
           (cond ((funcall test uuid-end)
514
                  (try-previous-transition (1- position<=)))
515
                 ((revision-coincident-p revision-designator uuid-end)
516
                  ;; if it is coincident, look for a previous interval
517
                  (let ((position-start (revision-transition-previous (1- position<=) sequence :transition-p (complement test))))
518
                    (when position-start
519
                      (try-previous-transition (1- position-start)))))
520
                 (t
521
                  ;; if the end precedes, find its start
522
                  (let ((position-start (revision-transition-previous (1- position<=) sequence :transition-p test)))
523
                    (when position-start
524
                      (values (aref sequence position-start) uuid-end))))))))))
525
 
526
 (defun search-revision-bounds<=  (revision-designator sequence &optional (test #'insert-uuid-p))
527
   "return start and end bounds for the interval ending at a revision < the given designator.
528
    the end is established by being accepted by a complement of the test.
529
    the default test locates insert-delete intervals."
530
   ;; in this case, the immediate position is itself the lower bounds
531
   (flet ((try-previous-transition (end)
532
            (let ((position< (revision-transition-previous end sequence :transition-p (complement test))))
533
              (when position<
534
                (let ((uuid-end (aref sequence position<)))  ;; no need to retest
535
                  (let ((position-start (revision-transition-previous (1- position<) sequence :transition-p test)))
536
                    (when position-start
537
                      (values (aref sequence position-start) uuid-end))))))))
538
     (let* ((position<= (position-uuid-vector revision-designator sequence)))
539
       (when position<=
540
         ;; some earlier transition exists
541
         (let ((uuid<= (aref sequence position<=)))
542
           ;; if it meets the test return the previous test/fail bounds
543
           ;; otherwise, iff it is previous, try it as end, otherwise locate its start and try before there
544
           (cond ((funcall test uuid<=)
545
                  (let ((position-end (revision-transition-next (1+ position<=) sequence :transition-p (complement test))))
546
                    (values uuid<= (when position-end (aref sequence position-end)))))
547
                 (t
548
                  ;; if the end precedes, find its start
549
                  (let ((position-start (revision-transition-previous (1- position<=) sequence :transition-p test)))
550
                    (when position-start
551
                      (values (aref sequence position-start) uuid<=))))))))))
552
 
553
 (defun search-revision-bounds>  (revision-designator sequence &optional (test #'insert-uuid-p))
554
   "return start and end bounds for the interval starting at a revision > the given designator
555
    the start is established by being accepted by the test.
556
    the default test locates insert-delete intervals."
557
   ;; in this case, the immediate position is itself the lower bounds
558
   (flet ((try-next-transition (start)
559
            (let ((position-> (revision-transition-next start sequence :transition-p test)))
560
              (when position->
561
                (let ((uuid-start (aref sequence position->)))  ;; no need to retest
562
                  (let ((position-end (revision-transition-next (1+ position->) sequence :transition-p (complement test))))
563
                    (values uuid-start (when position-end (aref sequence position-end)))))))))
564
     (let* ((position<= (position-uuid-vector revision-designator sequence)))
565
       (cond (position<=
566
              ;; some earlier transition exists
567
              (let ((uuid-start (aref sequence position<=)))
568
                ;; if it fails the test, return the next test/fail bounds
569
                ;; otherwise, locate its end and try from there
570
                (cond ((not (funcall test uuid-start))
571
                       (try-next-transition (1+ position<=)))
572
                      (t
573
                       (let ((position-end (revision-transition-next (1+ position<=) sequence :transition-p (complement test))))
574
                         (when position-end
575
                           (try-next-transition (1+ position-end))))))))
576
             ((> (length sequence) 0)
577
              ;; no earlier transition exists, look for from the first
578
              (try-next-transition 0))))))
579
            
580
 
581
 
582
 (defun match-statement-visibility-tail (continue map)
583
   "if visible at all, return the first location and possible interval bounds"
584
   (when (plusp (length map))
585
     (let* ((first-set-end (revision-transition-next 0 map :transition-p #'identity))
586
            (start-uuid (aref map first-set-end)))
587
       (when (insert-uuid-p start-uuid)
588
         (loop for end-position = (revision-transition-next (1+ first-set-end) map :transition-p #'delete-uuid-p)
589
           while end-position
590
           for end-uuid = (aref map end-position)
591
           when (delete-uuid-p end-uuid)
592
           return (funcall continue start-uuid end-uuid))))))
593
 
594
 
595
 
596
 (defun match-statement-visibility-after (continue vector start &optional (end start))
597
   "return the first interval visible after, but not at, end:
598
    - the @location or later must be a delete
599
    - after that, there must be a subsequent insert. if there is, it will be after rather than met-by
600
      as coincident insert/delete assertions resolve to one delete.
601
    return the visible bounds."
602
   (when end
603
     (multiple-value-bind (stmt-start stmt-end) (search-revision-bounds> end vector)
604
       (when stmt-start
605
         (funcall continue stmt-start stmt-end)))))
606
 
607
 (defun match-statement-visibility-before (continue vector start &optional (end start))
608
   "return the last interval visible before, but not at, start
609
    - the assertion @location must be delete
610
    - there must be a previous insert. if there is, the delete@location must precede the location timestamp
611
    return the visible bounds"
612
   (declare (ignore end))
613
   (multiple-value-bind (stmt-start stmt-end) (search-revision-bounds< start vector)
614
     (when (and stmt-end (revision-before-p stmt-end start))
615
       (funcall continue stmt-start stmt-end))))
616
 
617
 
618
 (defun match-statement-visibility-contains (continue vector start &optional (end start))
619
   "visible before start and after end, but not at either
620
    return the visible bounds"
621
   (when end
622
     ;; cannot contain unbounded interval
623
     (multiple-value-bind (stmt-start stmt-end) (search-revision-bounds<= end vector)
624
       (when (and stmt-start
625
                  (revision-before-p stmt-start start)
626
                  (or (null stmt-end)
627
                      (revision-before-p end stmt-end)))
628
         (funcall continue stmt-start stmt-end)))))
629
 
630
 (defun match-statement-visibility-disjoint (continue vector start &optional (end start))
631
   "return some interval visible before or after but not during
632
    return the previous or following"
633
   (multiple-value-bind (stmt-start stmt-end) (search-revision-bounds< start vector)
634
     (if (and stmt-end (revision-before-p stmt-end start))
635
         (funcall continue stmt-start stmt-end)
636
         (when end
637
           (multiple-value-bind (stmt-start stmt-end) (search-revision-bounds> end vector)
638
             (when stmt-start
639
               (funcall continue stmt-start stmt-end)))))))
640
 
641
 
642
 (defun match-statement-visibility-during (continue vector start &optional (end start))
643
   "return the first interval visible after start and before end but neither visible at start nor at end."
644
   (multiple-value-bind (stmt-start stmt-end) (search-revision-bounds<= start vector)
645
     (when (and stmt-start
646
                stmt-end ; must be bounded
647
                (revision-after-p stmt-start start)
648
                (or (null end) (revision-before-p stmt-end end)))
649
       (funcall continue stmt-start stmt-end))))
650
 
651
 (defun match-statement-visibility-equals (continue vector start &optional (end start))
652
   "visible with equal bounds
653
    yield the bounds"
654
   (when end
655
     (multiple-value-bind (stmt-start stmt-end) (search-revision-bounds<= start vector)
656
       (when (and stmt-start (revision-coincident-p stmt-start start)
657
                  stmt-end (revision-coincident-p stmt-end end))
658
         (funcall continue stmt-start stmt-end)))))
659
 
660
 
661
 (defun match-statement-visibility-finished-by (continue vector start &optional (end start))
662
   "visible before start and up to end
663
    yield the visible bounds"
664
   (when end
665
     (multiple-value-bind (stmt-start stmt-end) (search-revision-bounds<= start vector)
666
       (when (and stmt-start (revision-before-p stmt-start start)
667
                  stmt-end (revision-coincident-p stmt-end end))
668
         (funcall continue stmt-start stmt-end)))))
669
 
670
 (defun match-statement-visibility-finishes (continue vector start &optional (end start))
671
   "visible after start and up to end
672
    yield the visible bounds"
673
   (when end
674
     (let* ((position<= (position-uuid-vector end vector)))
675
       (when position<=
676
         ;; some earlier transition exists
677
         (let ((stmt-end (aref vector position<=)))
678
           ;; if it meets the test return the previous test/fail bounds
679
           ;; otherwise, iff it is previous, try it as end, otherwise locate its start and try before there
680
           (when (and (delete-uuid-p stmt-end(revision-coincident-p end stmt-end))
681
             (let ((position-start (revision-transition-previous (1- position<=) vector :transition-p #'insert-uuid-p)))
682
               (when position-start
683
                 (funcall continue (aref vector position-start) stmt-end)))))))))
684
 
685
 
686
 (defun match-statement-visibility-in (continue vector start &optional (end start))
687
   "visible at start or later and at end or earlier, but not both at
688
    return the first visible bounds
689
    https://www.w3.org/TR/owl-time/#time:intervalIn"
690
   (when end
691
     (multiple-value-bind (stmt-start stmt-end) (search-revision-bounds>= start vector)
692
       (when (and stmt-start
693
                  stmt-end
694
                  (if (revision-coincident-p stmt-start start)
695
                      (revision-before-p stmt-end end)
696
                      (not (revision-after-p stmt-end end))))
697
         (funcall continue stmt-start stmt-end)))))
698
 
699
 (defun match-statement-visibility-included-by (continue vector start &optional (end start))
700
   "visible on or after start and up to at or before end
701
    yield the visible bounds"
702
   (multiple-value-bind (stmt-start stmt-end) (search-revision-bounds>= start vector)
703
     (when (and stmt-start stmt-end
704
                (or (null end) (not (revision-after-p stmt-end end))))
705
           (funcall continue stmt-start stmt-end))))
706
 
707
 (defun match-statement-visibility-includes (continue vector start &optional (end start))
708
   "visible at or before start and up to or after end
709
    yield the visible bounds"
710
   (when end
711
     (multiple-value-bind (stmt-start stmt-end) (search-revision-bounds<= start vector)
712
       (when (and stmt-start
713
                  (or (null stmt-end) (not (revision-before-p stmt-end end))))
714
         (funcall continue stmt-start stmt-end)))))
715
 
716
 
717
 (defun match-statement-visibility-meets (continue vector start &optional (end start))
718
   "visible up to start
719
    return the first visible bounds"
720
   (multiple-value-bind  (stmt-start stmt-end) (search-revision-bounds< start vector)
721
     (when (and stmt-end (revision-coincident-p stmt-end end))
722
       (funcall continue stmt-start stmt-end))))
723
 
724
 (defun match-statement-visibility-met-by (continue vector start &optional (end start))
725
   "visible up to start
726
    yield the first visible bounds"
727
   (when end
728
     (multiple-value-bind (stmt-start stmt-end) (search-revision-bounds>= end vector)
729
       (when (and stmt-start (revision-coincident-p stmt-start end))
730
         (funcall continue stmt-start stmt-end)))))
731
 
732
 
733
 (defun match-statement-visibility-overlapped-by (continue vector start &optional (end start))
734
   "visible within bounds and after
735
    yield the visible bounds
736
    https://www.w3.org/TR/owl-time/#time:intervalOverlappedBy"
737
   (when end
738
     (multiple-value-bind (stmt-start stmt-end) (search-revision-bounds<= end vector)
739
       (when (and stmt-start stmt-end
740
                  (revision-after-p stmt-start start)
741
                  (revision-before-p stmt-start end)
742
                  (revision-after-p stmt-end end))
743
         (funcall continue stmt-start stmt-end)))))
744
 
745
 (defun match-statement-visibility-overlaps (continue vector start &optional (end start))
746
   "visible within bounds and before
747
    yield the visible bounds
748
    https://www.w3.org/TR/owl-time/#time:intervalOverlaps"
749
   (multiple-value-bind (stmt-start stmt-end) (search-revision-bounds<= vector start)
750
     (when (and stmt-start stmt-end
751
                (revision-before-p stmt-start start)
752
                (or (null end) (revision-before-p stmt-end end)))
753
       (funcall continue stmt-start stmt-end))))
754
 
755
 
756
 (defun match-statement-visibility-started-by (continue vector start &optional (end start))
757
   "visible from start until after end
758
    yield the visible bounds"
759
   (when end
760
     (multiple-value-bind (stmt-start stmt-end) (search-revision-bounds>= vector start)
761
       (when (and stmt-start (revision-coincident-p stmt-start start)
762
                  (or (null stmt-end) (revision-after-p stmt-end end)))
763
       (funcall continue stmt-start stmt-end)))))
764
 
765
 (defun match-statement-visibility-starts (continue vector start &optional (end start))
766
   "visible from start until before end
767
    yield the visible bounds"
768
   (multiple-value-bind (stmt-start stmt-end) (search-revision-bounds>= vector start)
769
       (when (and stmt-start (revision-coincident-p stmt-start start)
770
                  (or (null end) (revision-before-p stmt-end end)))
771
       (funcall continue stmt-start stmt-end))))
772
 
773
 
774
 
775
 #+(or) ;; no longer used
776
 (defun test-visibility-map (continue-op map relation)
777
   (declare (dynamic-extent continue-op))
778
   (destructuring-bind (op . args) relation
779
     (ecase op
780
       (|time|:|intervalAfter|        (apply #'match-statement-visibility-after continue-op map args))
781
       (|time|:|intervalBefore|       (apply #'match-statement-visibility-before continue-op map args))
782
       (|time|:|intervalContains|     (apply #'match-statement-visibility-contains continue-op map args))
783
       (|time|:|intervalDisjoint|     (apply #'match-statement-visibility-disjoint continue-op map args))
784
       (|time|:|intervalDuring|       (apply #'match-statement-visibility-during continue-op map args))
785
       (|time|:|intervalEquals|       (apply #'match-statement-visibility-equals continue-op map args))
786
       (|time|:|intervalFinishedBy|   (apply #'match-statement-visibility-finished-by continue-op map args))
787
       (|time|:|intervalFinishes|     (apply #'match-statement-visibility-finishes continue-op map args))
788
       (|time|:|intervalIncludedBy|   (apply #'match-statement-visibility-included-by continue-op map args))
789
       (|time|:|intervalIncludes|     (apply #'match-statement-visibility-includes continue-op map args))
790
       (|time|:|intervalIn|           (apply #'match-statement-visibility-in continue-op map args))
791
       (|time|:|intervalMeets|        (apply #'match-statement-visibility-meets continue-op map args))
792
       (|time|:|intervalMetBy|        (apply #'match-statement-visibility-met-by continue-op map args))
793
       (|time|:|intervalOverlappedBy| (apply #'match-statement-visibility-overlapped-by continue-op map args))
794
       (|time|:|intervalOverlaps|     (apply #'match-statement-visibility-overlaps continue-op map args))
795
       (|time|:|intervalStartedBy|    (apply #'match-statement-visibility-started-by continue-op map args))
796
       (|time|:|intervalStarts|       (apply #'match-statement-visibility-starts continue-op map args))
797
       ((spocq.a:|and| spocq.a:|&&|)
798
         (destructuring-bind (first &rest rest) args
799
           (if rest
800
               (flet ((continue-op (start end)
801
                        (declare (ignore start end))
802
                        (test-visibility-map continue-op map `(spocq.a:|and| ,@rest))))
803
                 (declare (dynamic-extent #'continue-op))
804
                 (test-visibility-map #'continue-op map first))
805
               (test-visibility-map continue-op map first))))
806
       ((spocq.a:|or| spocq.a:|\|\||)
807
         (flet ((continue-op (start end)
808
                  (return-from test-visibility-map (funcall continue-op start end))))
809
           (declare (dynamic-extent #'continue-op))
810
           (loop for expression in args
811
             do (test-visibility-map #'continue-op map expression))))
812
       ((spocq.a:|not| spocq.a:|!|)
813
          (flet ((continue-op (start end)
814
                   (declare (ignore start end))
815
                   (return-from test-visibility-map nil)))
816
            (declare (dynamic-extent #'continue-op))
817
            (test-visibility-map #'continue-op map (first args)))
818
          ;; if it does not invoke the continueation
819
          (match-statement-visibility-tail continue-op map)))))
820
 
821
 #|
822
 each statement is associated with a revision sequence vector which represents the merged state of that statement.
823
 the actualy sequence is not stored for each statement.
824
 in its stead, the statement's index value is the sequence sha1 hash.
825
 the hash value, in turn, locates the actual sequence in the respective repository's revision sequence database.
826
 
827
 in order to retrieve the statements for a given pattern, the pattern locates and constrains a scan over the statements index values.
828
 the abstract process is the same as for an isolated repository.
829
 in the replated case, however, each index entry is used, first to check in a transaction cache for a visibility determination.
830
 the cache key is the native index entry, rather than the sha1, as the entry remains stable for the transaction duration.
831
 if a cache entry is present, that determines the respective statement visibilty.
832
 if no cache entry is present, the actual sha1 hash is decoded and used to retrieve the complete revision sequence.
833
 this is used to test visibility given the transaction revision - latest or temporally significant.
834
 the eventual determination is then cached per sequence sha1 and then determines visibility.
835
 
836
 in order to insert a statement, the given quad value locates the revision sequence sha1 id.
837
 if none is present a unary sequence is re-used or created on demand.
838
 if a sha1 id is present, the sha1 is used to check a revision cache for a replacement sha1 id.
839
 if the replacement is known, that is written back as the replacement index value.
840
 if none is present, the complete revision sequence is retrieved, the new revision is merged into it,
841
 the sha1 is computed and cached and written back as the replacement index value.
842
 !
843
 |#
844
 
845
 
846
 (defgeneric replica-get-revision-sequence (repository %index-sha1 &key if-does-not-exist)
847
   (:method ((repository rlmdb::replicable-repository) %revision-sequence-id &key (if-does-not-exist :error))
848
     (with-lmdb-values ((%key spocq.i::+rsid-length+ %revision-sequence-id)
849
                        (%value 0 (cffi:null-pointer)))
850
       (let* ((db (repository-revision-sequence-database repository))
851
              (return-code (liblmdb:get (lmdb::handle lmdb:*transaction*) (lmdb::handle db) %key %value)))
852
         (cond ((zerop return-code)
853
                (%decode-revision-sequence (%mdb-val-data %value) (%mdb-val-size %value)))
854
               ((eql return-code liblmdb:+notfound+)
855
                (ecase if-does-not-exist
856
                  (:error (error "replica-get-revision-sequence: revision-seqience-not-found"))
857
                  ((nil) nil)))
858
               (t
859
                (lmdb::unknown-error return-code)))))))
860
 
861
 
862
 (defgeneric replica-put-revision-sequence-id (repository revision-sequence-id revision-sequence)
863
   (:method ((repository rlmdb::replicable-repository) (revision-sequence-id vector) (revision-sequence vector))
864
     (cffi:with-foreign-pointer (%sequence-id spocq.i::+rsid-length+)
865
       (%encode-revision-sequence-id %sequence-id revision-sequence-id)
866
       #+(or)
867
       (format *trace-output* "~%rprsi: ~s [~{~2,'0x~}] ~{~% ~s~}~%"
868
               (type-of revision-sequence-id) (coerce revision-sequence-id 'list)
869
               (map 'list #'spocq.i::uuid-to-string revision-sequence))
870
       (replica-put-revision-sequence-id repository %sequence-id revision-sequence)))
871
 
872
   (:method ((repository rlmdb::replicable-repository) (%revision-sequence-id SB-SYS:SYSTEM-AREA-POINTER) (revision-sequence vector))
873
     (cffi:with-foreign-pointer (%revision-sequence (* (length revision-sequence) spocq.i::+uuid-length+))
874
       (%encode-revision-sequence %revision-sequence revision-sequence)
875
       (with-lmdb-values ((%key spocq.i::+rsid-length+ %revision-sequence-id)
876
                          (%value (* (length revision-sequence) 16) %revision-sequence))
877
         (let* ((db (repository-revision-sequence-database repository))
878
                (return-code (liblmdb:put (lmdb::handle lmdb:*transaction*) (lmdb::handle db) %key %value liblmdb:+NODUPDATA+)))
879
           (cond ((zerop return-code)
880
                  t)
881
                 (t
882
                  (lmdb::unknown-error return-code))))))))
883
 
884
 (defvar *operation-uuid*)
885
 (defvar *visibility-cache*)
886
 
887
 (defmethod rlmdb::replicable-repository-update-field ((repository rlmdb::replicable-repository)
888
                                                      (solution-field array)
889
                                                      *operation-uuid*)
890
   "replace each statement's index entry with the id for a revision sequence
891
 which has been augmented by the given operation id (= timestamp x state).
892
    start with index[0], compute the new sequence id and then replicate that
893
 across all indices. if the statement was present, the new sequence merges
894
 the new operation id into the old. where the statement is new, the sequence
895
 is just the new operation."
896
   (lmdb:with-transaction ((lmdb:transaction  (lmdb:make-transaction repository :flags 0))
897
                           :initial-disposition :begin :normal-disposition :commit)
898
     ;; cache the new revision sequence respective the old when combined with the operation-uuid
899
     (let ((*visibility-cache* (make-hash-table :test 'eql)))
900
       (flet ((insert-page (page)
901
                (assert-argument-type repository-insert-field page
902
                                      (or (simple-array fixnum (* 3)) (simple-array fixnum (* 4))))
903
                (spocq.i::trace-algebra replica-update-field (repository-id repository) solution-field (spocq.i::term-value-field page))
904
                (ecase (array-dimension solution-field 1)
905
                  (3 (spocq.i::do-solution-field (subject predicate object) solution-field
906
                                                 (when (and (/= subject 0) (/= predicate 0) (/= object 0))
907
                                                   (rlmdb::repository-insert-statement repository lmdb:transaction
908
                                                                                       rdfcache:*default-context-number* subject predicate object))))
909
                  (4 (spocq.i::do-solution-field (subject predicate object graph) page
910
                                                 (when (and (/= subject 0) (/= predicate 0) (/= object 0) (/= graph 0))
911
                                                   (rlmdb::repository-insert-statement repository lmdb:transaction
912
                                                                                       graph subject predicate object)))))
913
                (array-dimension solution-field 0)))
914
         (ecase (array-rank solution-field)
915
           (1 (loop for solution-field-page across solution-field
916
                sum (insert-page solution-field-page)))
917
           (2 (insert-page solution-field)))))))
918
 
919
 
920
 (defmethod rlmdb::repository-insert-statement
921
   ((repository rlmdb:replicable-repository) (transaction rlmdb:transaction) graph subject predicate object)
922
   (cffi:with-foreign-objects ((%key-quad '(:struct quad)))
923
     (with-lmdb-values ((%key (load-time-value (cffi:foreign-type-size '(:struct quad))) %key-quad)
924
                        (%value 0 (cffi:null-pointer)))
925
       #+(or) (print (list :key (%mdb-val-size %key) (%mdb-val-data %key) :key-quad %key-quad))
926
       (labels ((compute-new-rsid (%key)
927
                  (let ((db (aref (repository-quad-databases repository) 0)))
928
                    (let ((return-code (liblmdb:get (lmdb::handle transaction) (lmdb::handle db) %key %value)))
929
                      #+(or) (print (list :cnr return-code))
930
                      (cond ((zerop return-code)
931
                             ;; merge the revision-op into an existing sequence
932
                             (assert (= (%mdb-val-size %value) spocq.i::+rsid-length+) ()
933
                                     "repository-update-field: invalid revision sequence id size: ~s"
934
                                     (%mdb-val-size %value))
935
                             (let* ((%old-rsid  (%mdb-val-data %value))
936
                                    (%old-rsid-addr (cffi:pointer-address %old-rsid))  ;; cached based on the prt address
937
                                    (cached-rsid (gethash %old-rsid-addr *visibility-cache*)))
938
                               (or cached-rsid ;; use the cached hash sequence
939
                                   ;; or compute the new one based on the revision operation
940
                                   (let* ((revision-sequence (replica-get-revision-sequence repository %old-rsid))
941
                                          (new-revision-sequence (spocq.i::merge-uuid-visibility *operation-uuid* revision-sequence))
942
                                          (new-rsid (spocq.i::compute-revision-sequence-id new-revision-sequence)))
943
                                     (replica-put-revision-sequence-id repository new-rsid new-revision-sequence)
944
                                     (setf (gethash %old-rsid-addr *visibility-cache*) new-rsid)
945
                                     new-rsid))))
946
                            ((eql return-code liblmdb:+notfound+)
947
                             ;; add a new sequence
948
                             (let ((cached-rsid (gethash 0 *visibility-cache*)))
949
                               (or cached-rsid
950
                                   (let* ((revision-sequence (make-array 1 :initial-contents (list *operation-uuid*)))
951
                                          (new-rsid (spocq.i::compute-revision-sequence-id revision-sequence)))
952
                                     (replica-put-revision-sequence-id repository new-rsid revision-sequence)
953
                                     (setf (gethash 0 *visibility-cache*) new-rsid)))))
954
                            (t
955
                             (lmdb::unknown-error return-code))))))
956
                (wrap (term-number)
957
                  (if (< term-number 0)
958
                      (+ term-number #x100000000)
959
                      term-number)))
960
         (setf (%quad-context %KEY-quad) (wrap graph)
961
               (%quad-subject %KEY-quad)  (wrap subject)
962
               (%quad-predicate %KEY-quad) (wrap predicate)
963
               (%quad-object %KEY-quad) (wrap object))             
964
         (let* ((new-rsid (compute-new-rsid %key)))
965
           (cffi:with-foreign-pointer (%new-rsid spocq.i::+rsid-length+)
966
             (%encode-revision-sequence-id %new-rsid new-rsid)
967
             (loop for db across (repository-quad-databases repository)
968
               do (setf (%mdb-val-size %value) spocq.i::+rsid-length+
969
                        (%mdb-val-data %value) %new-rsid)
970
               ; do (print (list db :key (%mdb-val-size %key) (%mdb-val-data %key) :value (%mdb-val-size %value) (%mdb-val-data %value)))
971
               do (let ((return-code (liblmdb:put (lmdb::handle transaction) (lmdb::handle db) %key %value 0)))
972
                    (unless (zerop return-code)
973
                      (lmdb::unknown-error return-code)))))))
974
     t)))
975
 
976
 (defgeneric rlmdb::repository-accept-field (repository source)
977
   (:documentation "Given a function or sequence source, iterate over the statements
978
 and insert/delete each as per specified operation.
979
 A function source should be an operator for two arguments (operation-continuation statement-continuation).
980
 It is invoked in the context of a transaction to provide the operation and the statements.
981
 
982
  The core operation is as for replicable-repository-update-field")
983
   
984
 
985
   (:method ((repository rlmdb:replicable-repository) (source function))
986
     (lmdb:with-transaction ((transaction  (lmdb:make-transaction repository :flags 0))
987
                           :initial-disposition :begin :normal-disposition :commit)
988
     ;; cache the new revision sequence respective the old when combined with the operation-uuid
989
     (let ((visibility-cache (make-hash-table :test 'eql))
990
           (operation-uuid nil)
991
           (x-record (when spocq.i::*transaction* (spocq.i::transaction-record spocq.i::*transaction*))))
992
       (cffi:with-foreign-objects ((%key-quad '(:struct rdfcache::quad)))
993
         (with-lmdb-values ((%key 16 %key-quad)
994
                            (%value 0 (cffi:null-pointer)))
995
           (labels ((statement-to-key (graph subject predicate object %quad)
996
                      (setf (%quad-context %quad) (wrap graph)
997
                            (%quad-subject %quad) (wrap subject)
998
                            (%quad-predicate %quad) (wrap predicate)
999
                            (%quad-object %quad) (wrap object)))
1000
                    (term-id (term)
1001
                      (case term
1002
                        ((nil) (error "term-id: invalid term: ~s." term))
1003
                        (|urn:dydra|:|all| spocq.i::*true-all-context-term-number*)
1004
                        (|urn:dydra|:|default| spocq.i::*true-default-context-term-number*)
1005
                        (|urn:dydra|:|named| spocq.i::*true-named-context-term-number*)
1006
                        (|urn:dydra|:|none| spocq.i::*true-none-context-term-number*)
1007
                        (t (spocq.i::rdfcache-object-term-number x-record term))))
1008
                    (graph-term-id (term)
1009
                      (case term
1010
                        ((nil) (spocq.i::symbol-term-id |urn:dydra|:|default|))
1011
                        (t (term-id term))))
1012
                    (compute-new-rsid (graph subject predicate object)
1013
                      (let ((db (aref (repository-quad-databases repository) 0)))
1014
                        (setf (%mdb-val-size %key) 16
1015
                              (%mdb-val-data %key) %key-quad)
1016
                        (statement-to-key graph subject predicate object %key-quad)
1017
                        (let ((return-code (liblmdb:get (lmdb::handle transaction) (lmdb::handle db) %key %value)))
1018
                          #+(or) (print (list :cnr return-code))
1019
                          (cond ((zerop return-code)
1020
                                 ;; merge the revision-op into an existing sequence
1021
                                 (assert (= (%mdb-val-size %value) spocq.i::+rsid-length+) ()
1022
                                         "repository-update-field: invalid revision sequence id size: ~s"
1023
                                         (%mdb-val-size %value))
1024
                                 (let* ((%old-rsid  (%mdb-val-data %value))
1025
                                        (%old-rsid-addr (cffi:pointer-address %old-rsid))  ;; cached based on the prt address
1026
                                        (cached-rsid (gethash %old-rsid-addr visibility-cache)))
1027
                                   (or cached-rsid ;; use the cached hash sequence
1028
                                       ;; or compute the new one based on the revision operation
1029
                                       (let* ((revision-sequence (replica-get-revision-sequence repository %old-rsid))
1030
                                              (new-revision-sequence (spocq.i::merge-uuid-visibility operation-uuid revision-sequence))
1031
                                              (new-rsid (spocq.i::compute-revision-sequence-id new-revision-sequence)))
1032
                                         (replica-put-revision-sequence-id repository new-rsid new-revision-sequence)
1033
                                         (setf (gethash %old-rsid-addr visibility-cache) new-rsid)
1034
                                         new-rsid))))
1035
                                ((eql return-code liblmdb:+notfound+)
1036
                                 ;; add a new sequence
1037
                                 (let ((cached-rsid (gethash 0 visibility-cache)))
1038
                                   (or cached-rsid
1039
                                       (let* ((revision-sequence (make-array 1 :initial-contents (list operation-uuid)))
1040
                                              (new-rsid (spocq.i::compute-revision-sequence-id revision-sequence)))
1041
                                         (replica-put-revision-sequence-id repository new-rsid revision-sequence)
1042
                                         (setf (gethash 0 visibility-cache) new-rsid)))))
1043
                                (t
1044
                                 (lmdb::unknown-error return-code))))))
1045
                    (wrap (term-number)
1046
                          (if (< term-number 0)
1047
                              (+ term-number #x100000000)
1048
                              term-number))
1049
                    (insert-statement (statement)
1050
                      (etypecase statement
1051
                        (cons (destructuring-bind (subject predicate object graph)
1052
                                                  (case (first statement)
1053
                                                    ((spocq.a:|quad| spocq.a:|triple|) (rest statement))
1054
                                                    (t statement))
1055
                                ;; shift graph term
1056
                                (insert-statement* (graph-term-id graph) (term-id subject) (term-id predicate) (term-id object))))
1057
                        (vector
1058
                         (insert-statement* (aref statement 0) (aref statement 1) (aref statement 2) (aref statement 3)))))
1059
                    (insert-statement* (graph subject predicate object)
1060
                      (statement-to-key graph subject predicate object %key-quad)
1061
                      (setf (%mdb-val-size %key) 16
1062
                            (%mdb-val-data %value) %key-quad)
1063
                      (let* ((new-rsid (compute-new-rsid graph subject predicate object)))
1064
                        (cffi:with-foreign-pointer (%new-rsid spocq.i::+rsid-length+)
1065
                          (%encode-revision-sequence-id %new-rsid new-rsid)
1066
                          (loop for db across (repository-quad-databases repository)
1067
                            do (setf (%mdb-val-size %value) spocq.i::+rsid-length+
1068
                                     (%mdb-val-data %value) %new-rsid)
1069
                            ; do (print (list db :key (%mdb-val-size %key) (%mdb-val-data %key) :value (%mdb-val-size %value) (%mdb-val-data %value)))
1070
                            do (progn 
1071
                                 (let ((return-code (liblmdb:put (lmdb::handle transaction) (lmdb::handle db) %key %value
1072
                                                                 liblmdb:+NODUPDATA+)))
1073
                                   (unless (zerop return-code)
1074
                                      (lmdb::unknown-error return-code))))))))
1075
                    (set-operation (new-operation-uuid)
1076
                      (setf operation-uuid new-operation-uuid)))
1077
             (funcall source #'set-operation #'insert-statement))))))))
1078
 
1079
 
1080
 ;; printing repository state
1081
 
1082
 (defgeneric map-revision-sequence-database (operator repository)
1083
   (:method (operator (repository rlmdb:replicable-repository))
1084
     (lmdb:with-transaction ((transaction (lmdb:make-transaction repository :flags 0))
1085
                             :initial-disposition :begin :normal-disposition :abort)
1086
       (map-revision-sequence-database operator (repository-revision-sequence-database repository))))
1087
 
1088
   (:method (operator (database rlmdb:revision-sequence-database))
1089
     (lmdb:ensure-open-database database)
1090
     (let* ((cur (lmdb:make-cursor database)))
1091
       (cffi:with-foreign-pointer (%sequence-id spocq.i::+rsid-length+)
1092
         (%encode-revision-sequence-id %sequence-id (make-array spocq.i::+rsid-length+ :element-type '(unsigned-byte 8) :initial-element 0))
1093
         (with-lmdb-value (%key spocq.i::+rsid-length+ %sequence-id)
1094
           (lmdb::with-empty-value (%data)
1095
             (lmdb:with-cursor (cur)
1096
               (labels ((do-rsid (get-op)
1097
                          (let ((return-code (liblmdb:cursor-get (lmdb::handle cur) %key %data get-op)))
1098
                            (alexandria:switch (return-code)
1099
                                               (0
1100
                                                (when (= (%mdb-val-size %key) spocq.i::+rsid-length+)
1101
                                                  (funcall operator
1102
                                                           (%decode-revision-sequence-id (%mdb-val-data %key))
1103
                                                           (%decode-revision-sequence (%mdb-val-data %data) (%mdb-val-size %data)))))
1104
                                               (liblmdb:+notfound+
1105
                                                nil)
1106
                                               (t
1107
                                                (lmdb::unknown-error return-code))))))
1108
                 
1109
                 (loop for op = :+set-range+ then :+next+
1110
                   with count = 0
1111
                   while (do-rsid op)
1112
                   do (incf count)
1113
                   finally (return count))))))))))
1114
 
1115
 
1116
 (defgeneric dump-repository-revision-sequence-index (repository &key stream verbose)
1117
   (:method ((repository rlmdb:replicable-repository) &key (stream *standard-output*) verbose)
1118
     (declare (ignore verbose))
1119
     (flet ((format-entry (rsid revision-sequence)
1120
              (format stream "~%[~{~2,'0x~}] (~{~/format-uuid-operation/~^~%~44T~})"
1121
                      (coerce rsid 'list)
1122
                      (coerce revision-sequence 'list))
1123
              t))
1124
       (map-revision-sequence-database #'format-entry repository))))
1125
 
1126
 
1127
 (defgeneric dump-repository-statement-index (repository &key stream verbose)
1128
   (:method ((repository rlmdb:replicable-repository) &key (stream *standard-output*) (verbose nil))
1129
     (flet ((report-index-entry (%index-quad %index-rsid)
1130
              (let ((rsid (%decode-revision-sequence-id %index-rsid)))
1131
                (if verbose
1132
                    (let ((revision-sequence (replica-get-revision-sequence repository %index-rsid))
1133
                          (quad (term-number-record-to-quad %index-quad (spocq:make-quad))))
1134
                      (format stream "~%~%(~s ~s ~s ~s)~%[~{~2,'0x~}] (~{~/format-uuid-operation/~^~%~44T~})"
1135
                              (spocq:quad-subject quad) (spocq:quad-predicate quad) (spocq:quad-object quad) (spocq:quad-graph quad)
1136
                              (coerce rsid 'list)
1137
                              (coerce revision-sequence 'list)))
1138
                    (format stream "~%~/%format-quad/ [~{~2,'0x~}]"
1139
                              %index-quad
1140
                              (coerce rsid 'list)))
1141
                t)))
1142
       (declare (dynamic-extent #'report-index-entry))
1143
       (rlmdb:map-repository-statements #'report-index-entry repository #(0 0 0 0)))))
1144
     
1145
 
1146
 (defgeneric rlmdb::dump-repository (repository &key stream verbose)
1147
   (:documentation "dump out the individual database contents for the given repository type:
1148
 - plain: the metadata and the spog index
1149
 - revisioned: the metadata, revision sequence and spog index
1150
 - replicable: the metadata, revision history, revision sequence id index and spog index.")
1151
 
1152
   (:method ((repository-id string) &rest args)
1153
     (apply #'rlmdb::dump-repository (spocq.i::repository repository-id) args))
1154
 
1155
   (:method ((repository rlmdb:replicable-repository) &key (stream *standard-output*) (verbose nil))
1156
     (let ((*print-pretty* nil))
1157
       (format stream "~%metadata~{~% ~s~}" (rlmdb:get-metadata repository))
1158
       (format stream "~%revision records~{~% ~s~}" (rlmdb:revision-records repository))
1159
       (format stream "~%revision sequence index")
1160
       (dump-repository-revision-sequence-index repository :stream stream :verbose verbose)
1161
       (format stream "~%spog index")
1162
       (dump-repository-statement-index repository :stream stream :verbose verbose))))
1163
 
1164
 
1165
 ;;; compute size
1166
 
1167
 (defmethod spocq.i::compute-repository-size ((repository rlmdb:replicable-repository) &key (literal-term-size 0) (verbose nil))
1168
     (declare (ignore literal-term-size))
1169
   (lmdb:with-transaction ((transaction  (lmdb:make-transaction repository #|:flags liblmdb:+rdonly+|#))
1170
                             :initial-disposition :begin :normal-disposition :abort)
1171
       ;; no graph database support
1172
       (let* ((repository-id (repository-id repository))
1173
              (index-database (repository-quad-pattern-index repository #(0 0 0 0)))
1174
              (sequence-database (repository-revision-sequence-database repository))
1175
              (sequence-id-cache (make-hash-table :test #'equalp))
1176
              (revision-sequence-total-count 0)
1177
              (revision-sequence-total-bytes 0)
1178
              (revision-sequence-referenced-count 0)
1179
              (revision-sequence-referenced-bytes 0)
1180
              (statement-count 0)
1181
              (statement-count-versioned 0)
1182
              (term-bytes 0)
1183
              (index-leaf-bytes 0)
1184
              (index-node-bytes 0)
1185
              (index-bytes 0)
1186
              (index-count 0)
1187
              (term-count 0)
1188
              (terms (make-hash-table :test 'equal))
1189
              (strings (make-hash-table :test 'equal)))
1190
         (flet ((stat-size ()
1191
                  (let ((process (run-program "/bin/bash" `("/opt/rails/script/repository_size" ,repository-id) :wait t :output :stream)))
1192
                    (assert process () "no process for size: ~s" repository-id)
1193
                    (unwind-protect (parse-integer (read-line (run-program-output process)) :junk-allowed t)
1194
                      (close (run-program-output process))
1195
                      (run-program-close process))))
1196
                (compressed-size ()
1197
                  (let ((process (run-program "/bin/bash" `("/opt/rails/script/repository_compressed_size" ,repository-id) :wait t :output :stream)))
1198
                    (assert process () "no process for size: ~s" repository-id)
1199
                    (unwind-protect (parse-integer (read-line (run-program-output process)) :junk-allowed t)
1200
                      (close (run-program-output process))
1201
                      (run-program-close process))))
1202
                (size-index-database ()
1203
                  (labels ((term-size (term-number)
1204
                             (if (= 4294967295 term-number)
1205
                                 (if (gethash (iri-lexical-form |urn:dydra|:|default|) strings)
1206
                                     0
1207
                                     (setf (gethash (iri-lexical-form |urn:dydra|:|default|) strings)
1208
                                           (length (iri-lexical-form |urn:dydra|:|default|)))) 
1209
                                 (destructuring-bind (&key type value-string language-string datatype-string value)
1210
                                                     (rlmdb:term-elements term-number)
1211
                                   (declare (ignore type value))
1212
                                   (+ (if value-string (length value-string) 0)
1213
                                      (if language-string
1214
                                          (if (gethash language-string strings) 0
1215
                                              (setf (gethash language-string strings) (length language-string)))
1216
                                          0)
1217
                                      (if datatype-string
1218
                                          (if (gethash datatype-string strings) 0
1219
                                              (setf (gethash datatype-string strings) (length datatype-string)))
1220
                                          0)
1221
                                      12))))
1222
                           (collect-term (term-number)
1223
                             (unless (gethash term-number terms)
1224
                               (setf (gethash term-number terms) (incf term-count))))
1225
                           (collect-index-entry (%quad sha-1)
1226
                             (setf sha-1 (copy-seq sha-1))
1227
                             (collect-term (%quad-context %quad))
1228
                             (collect-term (%quad-subject %quad))
1229
                             (collect-term (%quad-predicate %quad))
1230
                             (collect-term (%quad-object %quad))
1231
                             (incf statement-count)
1232
                             (incf index-count)
1233
                             (incf index-leaf-bytes (load-time-value (+ (* 4 (CFFI:FOREIGN-TYPE-SIZE :short))
1234
                                                                        (cffi:foreign-type-size '(:struct quad))))) ;; leaf node
1235
                             (incf index-leaf-bytes (length sha-1))
1236
                             (incf (gethash sha-1 sequence-id-cache 0))))
1237
                    (declare (dynamic-extent #'collect-index-entry))
1238
                    (map-repository-index #'collect-index-entry index-database)
1239
                    (loop for term-number being each hash-key of terms
1240
                      do (incf term-bytes (term-size term-number)))
1241
                    (setf index-node-bytes (* (load-time-value (+ (* 4 (CFFI:FOREIGN-TYPE-SIZE :short))
1242
                                                                  (cffi:foreign-type-size '(:struct quad))))
1243
                                              (loop for inner-count = (floor index-count 2) then (floor inner-count 2) until (= inner-count 0) sum inner-count)))
1244
                    ;; multiply by number of indeces
1245
                    (setf index-node-bytes (* index-node-bytes (length +quad-database-names+)))
1246
                    (setf index-leaf-bytes (* index-leaf-bytes (length +quad-database-names+)))
1247
                    (setf index-bytes (+ index-node-bytes index-leaf-bytes))))
1248
 
1249
                (size-sequence-database ()
1250
                  (lmdb:ensure-open-database sequence-database)
1251
                  (let* ((cur (lmdb:make-cursor sequence-database)))
1252
                    (cffi:with-foreign-pointer (%sequence-id spocq.i::+rsid-length+)
1253
                      (%encode-revision-sequence-id %sequence-id (make-array spocq.i::+rsid-length+ :element-type '(unsigned-byte 8) :initial-element 0))
1254
                      (with-lmdb-value (%key spocq.i::+rsid-length+ %sequence-id)
1255
                        (lmdb::with-empty-value (%data)
1256
                          (lmdb:with-cursor (cur)
1257
                            (labels ((do-rsid (get-op)
1258
                                       (let ((return-code (liblmdb:cursor-get (lmdb::handle cur) %key %data get-op)))
1259
                                         (alexandria:switch (return-code)
1260
                                                            (0
1261
                                                             (when (= (%mdb-val-size %key) spocq.i::+rsid-length+)
1262
                                                               (let* ((sequence-id (%decode-revision-sequence-id (%mdb-val-data %key)))
1263
                                                                      (sequence (%decode-revision-sequence (%mdb-val-data %data) (%mdb-val-size %data)))
1264
                                                                      (bytes (+ (length sequence-id) (* (length sequence) 16))))
1265
                                                                 (incf revision-sequence-total-count)
1266
                                                                 (incf revision-sequence-total-bytes bytes)
1267
                                                                 (cond ((gethash sequence-id sequence-id-cache)
1268
                                                                        (incf revision-sequence-referenced-count)
1269
                                                                        (incf revision-sequence-referenced-bytes bytes))
1270
                                                                       (t
1271
                                                                        (liblmdb:del (lmdb::handle transaction)
1272
                                                                                     (lmdb::handle sequence-database)
1273
                                                                                     %key
1274
                                                                                     %data))))
1275
                                                               t))
1276
                                                            (liblmdb:+notfound+
1277
                                                             nil)
1278
                                                            (t
1279
                                                             (lmdb::unknown-error return-code))))))
1280
                              (loop for op = :+set-range+ then :+next+
1281
                                while (do-rsid op))))))))))
1282
           (size-index-database)
1283
           (size-sequence-database)
1284
           (when verbose (print (list :sequence-id-cache sequence-id-cache)))
1285
           (let ((stat-bytes (stat-size))
1286
                 (stat-compressed-bytes (compressed-size)))
1287
             (list repository-id
1288
                   :STATEMENT-COUNT statement-count
1289
                   :STATEMENT-COUNT-VERSIONED statement-count-versioned
1290
                   :TERMS (hash-table-count terms)
1291
                   :INDEX-BYTES index-bytes
1292
                   :INDEX-LEAF-BYTES index-leaf-bytes
1293
                   :INDEX-NODE-BYTES index-node-bytes
1294
                   :TERM-BYTES term-bytes
1295
                   :STAT-BYTES stat-bytes
1296
                   :STAT-COMPRESSED-BYTES stat-compressed-bytes
1297
                   :revision-sequence-total-count revision-sequence-total-count
1298
                   :revision-sequence-total-bytes revision-sequence-total-bytes
1299
                   :revision-sequence-referenced-count revision-sequence-referenced-count
1300
                   :revision-sequence-referenced-bytes revision-sequence-referenced-bytes
1301
                   :TOTAL-BYTES (+ term-bytes stat-compressed-bytes)))))))
1302
 
1303
 
1304
 (defmethod map-repository-index (operator (index rlmdb::replicable-quad-database))
1305
   "iterate over the entire index."
1306
   (lmdb:with-database (index)
1307
     (let* ((cur (lmdb:make-cursor index :transaction lmdb:*transaction*))
1308
            (test-rsid (make-array spocq.i::+rsid-length+ :element-type 'unsigned-byte))
1309
            (quad-pattern #(0 0 0 0)))
1310
       ;;(let ((%key-quad (cffi:foreign-alloc '(:struct rdfcache::quad))))
1311
       (cffi:with-foreign-objects ((%quad-pattern '(:struct rdfcache::quad))
1312
                                   (%key-quad '(:struct rdfcache::quad)))
1313
         (lmdb::with-empty-value (%key)
1314
           (lmdb::with-empty-value (%value)
1315
             (quad-to-quad-record quad-pattern %quad-pattern)
1316
             (%copy-quad %quad-pattern %key-quad)
1317
             (lmdb:with-cursor (cur)
1318
               (let ((%cursor (lmdb::handle cur)))
1319
                 (labels ((get-quad (get-op)
1320
                            (ecase get-op
1321
                              (:+set-range+
1322
                               (setf (%mdb-val-size %key) 16
1323
                                     (%mdb-val-data %key) %key-quad))
1324
                              (:+next+ ))
1325
                            (let ((return-code (liblmdb:cursor-get %cursor %key %value get-op)))
1326
                              (alexandria:switch (return-code)
1327
                                                 (0
1328
                                                  (call-with-quad-entry %key %value))
1329
                                                 (liblmdb:+notfound+
1330
                                                  nil)
1331
                                                 (t
1332
                                                  (lmdb::unknown-error return-code)))))
1333
                          (call-with-quad-entry (%k %v)
1334
                            (assert (= 16 (cffi:pointer-address (cffi:foreign-slot-value %k '(:struct liblmdb:val) 'liblmdb:mv-size)))
1335
                                    ()
1336
                                    "key size is invalid: ~s" (cffi:pointer-address (cffi:foreign-slot-value %k '(:struct liblmdb:val) 'liblmdb:mv-size)))
1337
                            (let* ((%index-quad (%mdb-val-data %k))
1338
                                   (%index-sha1 (%mdb-val-data %v)))
1339
                              (funcall operator %index-quad (%decode-revision-sequence-id %index-sha1 test-rsid)))))
1340
                      (loop for op = :+set-range+ then :+next+
1341
                        with count = 0
1342
                        while (get-quad op)
1343
                        do (incf count)
1344
                        finally (return count)))))))))))
1345
 
1346
 #|
1347
 variants
1348
 
1349
 
1350
 ("bear/hour-replicated-patch" :STATEMENT-COUNT 0 :STATEMENT-COUNT-VERSIONED 0
1351
  :TERMS 299 :INDEX-BYTES 104304 :INDEX-LEAF-BYTES 67584 :INDEX-NODE-BYTES 36720
1352
  :TERM-BYTES 11744 :STAT-BYTES 203403264 :STAT-COMPRESSED-BYTES 139362304
1353
  :REVISION-SEQUENCE-TOTAL-COUNT 24901 :REVISION-SEQUENCE-TOTAL-BYTES 7912913
1354
  :REVISION-SEQUENCE-REFERENCED-COUNT 4 :REVISION-SEQUENCE-REFERENCED-BYTES 89
1355
  :TOTAL-BYTES 139374048)
1356
 * (in-package :rlmdb.i)
1357
 
1358
 (compute-repository-size "bear/hour-replicated-patch")
1359
 
1360
 
1361
 ;; just the first two patches
1362
 ("bear/hour-replicated-patch" :STATEMENT-COUNT 256 :STATEMENT-COUNT-VERSIONED 0
1363
  :TERMS 299 :INDEX-BYTES 104304 :INDEX-LEAF-BYTES 67584 :INDEX-NODE-BYTES 36720
1364
  :TERM-BYTES 11744 :STAT-BYTES 203403264 :STAT-COMPRESSED-BYTES 139362304
1365
  :REVISION-SEQUENCE-TOTAL-COUNT 24901 :REVISION-SEQUENCE-TOTAL-BYTES 119136308
1366
  :REVISION-SEQUENCE-REFERENCED-COUNT 4 :REVISION-SEQUENCE-REFERENCED-BYTES 224
1367
  :TOTAL-BYTES 139374048)
1368
 
1369
 the full 1298
1370
 ("bear/hour-replicated-patch" :STATEMENT-COUNT 134978
1371
  :STATEMENT-COUNT-VERSIONED 0 :TERMS 123009 :INDEX-BYTES 55070016
1372
  :INDEX-LEAF-BYTES 35634192 :INDEX-NODE-BYTES 19435824 :TERM-BYTES 5030976
1373
  :STAT-BYTES 341880832 :STAT-COMPRESSED-BYTES 338481152
1374
  :REVISION-SEQUENCE-TOTAL-COUNT 49762 :REVISION-SEQUENCE-TOTAL-BYTES 238270408
1375
  :REVISION-SEQUENCE-REFERENCED-COUNT 10552 :REVISION-SEQUENCE-REFERENCED-BYTES
1376
  783920 :TOTAL-BYTES 343512128)
1377
 
1378
 
1379
 |#
1380