root/trunk/bknr/datastore/src/data/blob.lisp

Revision 3942, 7.5 kB (checked in by hans, 2 months ago)

Merge from anon-transaction-fixes-2 branch. This changeset removes
make-object and initialize-persistent-instance, makes the allocation
of object IDs simpler and more safe and removes several relicts from
previous refactoring iterations. Also, the store tests have been
extended significantly to test pathological cases and create objects
from multiple threads.

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1 (in-package :bknr.datastore)
2
3 (cl-interpol:enable-interpol-syntax)
4
5 ;;; blob
6
7 (defclass blob (store-object)
8   ((type :initarg :type
9          :reader blob-type
10          :reader blob-mime-type
11          :index-type hash-index
12          :index-initargs (:test #'equal)
13          :index-reader blobs-with-type)
14    (timestamp :initarg :timestamp :reader blob-timestamp
15               :initform (get-universal-time)))
16   (:metaclass persistent-class))
17
18 #+nil
19 (define-persistent-class blob ()
20   ((type :read)
21    (timestamp :read))
22   (:default-initargs :timestamp (get-universal-time)))
23
24 (defmethod print-object ((object blob) stream)
25   (print-unreadable-object (object stream :type t)
26     (format stream "ID: ~D, TYPE: ~A"
27             (store-object-id object)
28             (if (slot-boundp object 'type) (blob-type object) "<not yet known>"))))
29
30 (defclass blob-subsystem ()
31   ((n-blobs-per-directory
32     :initform nil
33     :initarg :n-blobs-per-directory
34     :accessor n-blobs-per-directory
35     :documentation "The number of blobs to store in each subdirectory of
36 blob-root.  If this is NIL, do not create subdirectories.  This parameter
37 must be specified when the when the store is created and is stored in the
38 datastore root to ensure that the correct value is used later.")))
39
40 (defun store-blob-root-pathname (&optional (store *store*))
41   (merge-pathnames #p"blob-root/" (store-directory store)))
42
43 (defun store-blob-root-tempdir (&optional (store *store*))
44   (merge-pathnames #p"temp/" (store-blob-root-pathname store)))
45
46 (defmethod initialize-subsystem ((subsystem blob-subsystem) store store-existed-p)
47   (let* ((store-dir (store-current-directory store))
48          (nblobs-pathname
49           (make-pathname :name "n-blobs-per-directory" :defaults store-dir)))
50     (if store-existed-p
51         (if (probe-file nblobs-pathname)
52             (unless (eql (n-blobs-per-directory subsystem)
53                          (with-open-file (s nblobs-pathname)
54                            (read s)))
55               (error "BLOB configuration file ~A disagrees with user configuration"
56                      nblobs-pathname))
57             (progn
58               (warn "Could not find stored number of blobs per directory, writing current value: ~S"
59                     (n-blobs-per-directory subsystem))
60               (with-open-file (s nblobs-pathname :direction :output)
61                 (write (n-blobs-per-directory subsystem) :stream s))))
62         (with-open-file (s nblobs-pathname :direction :output)
63           (write (n-blobs-per-directory subsystem) :stream s)))))
64
65 (defun blob-subsystem ()
66   (or (find-if (lambda (subsystem)
67                  (typep subsystem 'blob-subsystem))
68                (store-subsystems *store*))
69       (error "store ~A does not have a BLOB subsysten" *store*)))
70
71 (defmethod initialize-instance :before ((blob blob) &rest args)
72   (declare (ignore args))
73   (unless (blob-subsystem)
74     (error "Can't create a BLOB in a datastore without BLOB subsystem.")))
75
76 (defmethod blob-relative-pathname (id)
77   (let ((n-files (n-blobs-per-directory (blob-subsystem))))
78     (if n-files
79         (make-pathname
80          :directory (list :relative (write-to-string (truncate id n-files)))
81          :name (write-to-string id))
82         (make-pathname :name (write-to-string id)))))
83
84 (defgeneric blob-pathname (blob-or-blob-id))
85
86 (defmethod blob-pathname ((id integer))
87   (ensure-directories-exist (merge-pathnames (blob-relative-pathname id)
88                                              (store-blob-root-pathname *store*)) :verbose t))
89
90 (defmethod blob-pathname ((blob blob))
91   (blob-pathname (store-object-id blob)))
92
93 (defmacro with-open-blob ((s blob &rest args) &rest body)
94   `(with-open-file (,s (blob-pathname ,blob) ,@args)
95      ,@body))
96
97 (defgeneric blob-size (blob))
98
99 (defmethod blob-size ((blob blob))
100   (with-open-blob (s blob)
101     (file-length s)))
102
103 (defgeneric blob-to-stream (blob s))
104
105 (defmethod blob-to-stream ((blob blob) out)
106   (with-open-blob (in blob :direction :input
107                       :element-type '(unsigned-byte 8))
108     (copy-stream in out)))
109
110 (defgeneric blob-to-file (blob pathname))
111
112 (defmethod blob-to-file ((blob blob) pathname)
113   (with-open-file (out pathname :direction :output :element-type '(unsigned-byte 8))
114     (blob-to-stream blob out)))
115
116 (defgeneric blob-from-stream (blob stream))
117
118 (defmethod blob-from-stream ((blob blob) in)
119   (with-open-blob (out blob :direction :output
120                        :element-type '(unsigned-byte 8)
121                        :if-exists :overwrite
122                        :if-does-not-exist :create)
123     (copy-stream in out)))
124
125 (defgeneric blob-from-string (blob string))
126
127 (defmethod blob-from-string ((blob blob) string)
128   (with-open-blob (out blob :direction :output
129                        :if-exists :overwrite
130                        :if-does-not-exist :create)
131     (write-string string out)))
132
133 (defgeneric blob-from-array (blob array))
134
135 (defmethod blob-from-array ((blob blob) in)
136   (with-open-blob (out blob :direction :output
137                        :element-type '(unsigned-byte 8)
138                        :if-exists :overwrite
139                        :if-does-not-exist :create)
140     (write-sequence in out)))
141
142 (defgeneric blob-from-file (blob pathname))
143
144 (defmethod blob-from-file ((blob blob) pathname)
145   (with-open-file (in pathname :direction :input :element-type '(unsigned-byte 8))
146     (blob-from-stream blob in)))
147
148 (defun make-blob-from-file (pathname &optional (class 'blob) &rest initargs)
149   (unless (getf initargs :type)
150     (setf (getf initargs :type)
151           (pathname-type pathname)))
152   (let ((blob (apply #'make-instance class initargs)))
153     (blob-from-file blob pathname)
154     blob))
155
156 (defmethod rename-file-to-blob ((blob blob) pathname)
157   (move-file pathname (blob-pathname blob)))
158
159 (defmethod restore-subsystem ((store store) (subsystem blob-subsystem) &key until)
160   (declare (ignore until))
161   ;; the blob subsystem does not do anything upon restore
162   )
163
164 (defmethod snapshot-subsystem ((store store) (subsystem blob-subsystem))
165   (let* ((store-dir (ensure-store-current-directory store))
166          (nblobs-pathname
167           (make-pathname :name "n-blobs-per-directory" :defaults store-dir)))
168     (with-open-file (s nblobs-pathname :direction :output)
169       (write (n-blobs-per-directory subsystem) :stream s))))
170
171 (defun delete-orphaned-blob-files (&optional (cold-run t))
172   (dolist (blob-pathname (directory (merge-pathnames (make-pathname :name :wild :directory '(:relative :wild-inferiors))
173                                                      (store-blob-root-pathname))))
174     (handler-case
175         (when (pathname-name blob-pathname)
176           (let* ((object-id (parse-integer (pathname-name blob-pathname)))
177                  (object (find-store-object object-id)))
178             (labels ((delete-orphan (pathname)
179                        (handler-case
180                            (if cold-run
181                                (format t "cold run, not deleting ~A~%" pathname)
182                                (delete-file pathname))
183                          (error (e)
184                            (warn "can't delete file ~A: ~A" pathname e)))))
185               (cond
186                 ((null object)
187                  (format t "; file ~A does not have a corresponding blob object - deleted~%" blob-pathname)
188                  (delete-orphan blob-pathname))
189                 ((not (subtypep (type-of object) 'blob))
190                  (format t "; file ~A has an object id of an object which does not have a subtype of blob (~A) - deleted~%"
191                          blob-pathname (type-of object))
192                  (delete-orphan blob-pathname))))))
193       (error (e)
194         (error "~A checking blob pathname ~A" e blob-pathname)))))
Note: See TracBrowser for help on using the browser.