diff options
Diffstat (limited to 'docs/storage.md')
-rw-r--r-- | docs/storage.md | 52 |
1 files changed, 40 insertions, 12 deletions
diff --git a/docs/storage.md b/docs/storage.md index f1de2db..794a739 100644 --- a/docs/storage.md +++ b/docs/storage.md | |||
@@ -33,7 +33,9 @@ A entity is uniquely identified by: | |||
33 | The additional revision identifies a specific instance/version of the entity. | 33 | The additional revision identifies a specific instance/version of the entity. |
34 | 34 | ||
35 | Uri Scheme: | 35 | Uri Scheme: |
36 | akonadi://resource/id:revision | 36 | ``` |
37 | akonadi://resource/id:revision | ||
38 | ``` | ||
37 | 39 | ||
38 | ## Store Entities | 40 | ## Store Entities |
39 | Each entity can be as normalized/denormalized as useful. It is not necessary to have a solution that fits everything. | 41 | Each entity can be as normalized/denormalized as useful. It is not necessary to have a solution that fits everything. |
@@ -64,7 +66,7 @@ Event { | |||
64 | } | 66 | } |
65 | ``` | 67 | ``` |
66 | 68 | ||
67 | Of course any combination of the two can be used, including duplicating data into individual properties while keeping the complete struct intact. The question then becomes though which copy is used for conflict resolution (perhaps this would result in more problems than it solves). | 69 | Of course any combination of the two can be used, including duplicating data into individual properties while keeping the complete struct intact. |
68 | 70 | ||
69 | #### Optional Properties | 71 | #### Optional Properties |
70 | For each domain type, we want to define a set of required and a set of optional properties. The required properties are the minimum bar for each resource, and are required in order for applications to work as expected. Optional properties may only be shown by the UI if actually supported by the backend. | 72 | For each domain type, we want to define a set of required and a set of optional properties. The required properties are the minimum bar for each resource, and are required in order for applications to work as expected. Optional properties may only be shown by the UI if actually supported by the backend. |
@@ -79,18 +81,22 @@ The advantage of this is that a resource only needs to specify a minimal set of | |||
79 | Each entity-value in the key-value store consists of the following individual buffers: | 81 | Each entity-value in the key-value store consists of the following individual buffers: |
80 | 82 | ||
81 | * Metadata: metadata that is required for every entity (revision, ....) | 83 | * Metadata: metadata that is required for every entity (revision, ....) |
82 | * Resource: the buffer defined by the resource (synchronized properties, values that help for synchronization such as remoteId's) | 84 | * Local: default storage buffer that is domain-type specific. |
83 | * Local-only: default storage buffer that is domain-type specific. | 85 | * Resource: the buffer defined by the resource (additional properties, values that help for synchronization) |
84 | 86 | ||
85 | ## Database | 87 | ## Database |
86 | ### Storage Layout | 88 | ### Database Layout |
87 | Storage is split up in multiple named databases that reside in the same database environment. | 89 | Storage is split up in multiple named databases that reside in the same database environment. |
88 | 90 | ||
89 | ``` | 91 | ``` |
90 | $DATADIR/storage/$RESOURCE_IDENTIFIER/$BUFFERTYPE.main | 92 | $DATADIR/storage/$RESOURCE_IDENTIFIER/ |
91 | $BUFFERTYPE.index.$INDEXTYPE | ||
92 | ``` | 93 | ``` |
93 | 94 | ||
95 | * $BUFFERTYPE.main: The primary store for a type | ||
96 | * $BUFFERTYPE.index.$PROPERTY: Secondary indexes | ||
97 | * revisionType: Allows to lookup the type by revision to find the correct primary or secondary db's. | ||
98 | * revisions: Allows to lookup the entity id by revision | ||
99 | |||
94 | The resource can be effectively removed from disk (besides configuration), | 100 | The resource can be effectively removed from disk (besides configuration), |
95 | by deleting the directories matching `$RESOURCE_IDENTIFIER*` and everything they contain. | 101 | by deleting the directories matching `$RESOURCE_IDENTIFIER*` and everything they contain. |
96 | 102 | ||
@@ -100,9 +106,25 @@ by deleting the directories matching `$RESOURCE_IDENTIFIER*` and everything they | |||
100 | ### Revisions | 106 | ### Revisions |
101 | Every operation (create/delete/modify), leads to a new revision. The revision is an ever increasing number for the complete store. | 107 | Every operation (create/delete/modify), leads to a new revision. The revision is an ever increasing number for the complete store. |
102 | 108 | ||
109 | Each entity is stored with a key consisting of its id and the revision. This way it is possible to lookup older revision. | ||
110 | |||
111 | Removing an entity simply results in a new revision of the entitiy recording the removal. | ||
112 | |||
113 | Secondary indexes always refer to the latest revision. | ||
114 | |||
103 | #### Design Considerations | 115 | #### Design Considerations |
104 | By having one revision for the complete store instead of one per type, the change replay always works across all types. This is especially important in the write-back mechanism that replays the changes to the source. | 116 | By having one revision for the complete store instead of one per type, the change replay always works across all types. This is especially important in the write-back mechanism that replays the changes to the source. |
105 | 117 | ||
118 | ### Revision cleanup | ||
119 | Because the store would grow infinitely, old revisions need to be removed. | ||
120 | The resource maintains a "lower bound revision", which is the lowest revision of any change-replaying component (such as clients and write-back). | ||
121 | For all lower revisions the cleanup will remove any revision that: | ||
122 | |||
123 | * is a delete command (the revision is no longer existing) | ||
124 | * has a newer revision for the same entity (the revision is outdated) | ||
125 | |||
126 | By doing cleanups continously, we avoid keeping outdated data. | ||
127 | |||
106 | ### BLOB properties | 128 | ### BLOB properties |
107 | Files are used to handle opaque large properties that should not end up in memory. BLOB properties are in their nature never queriable (extract parts of it to other properties if indexes are required). | 129 | Files are used to handle opaque large properties that should not end up in memory. BLOB properties are in their nature never queriable (extract parts of it to other properties if indexes are required). |
108 | 130 | ||
@@ -110,7 +132,7 @@ For reading: | |||
110 | 132 | ||
111 | Resources... | 133 | Resources... |
112 | 134 | ||
113 | * store the file in $DATADIR/storage/$RESOURCE_IDENTIFIER_files/ | 135 | * store the file in $DATADIR/storage/$RESOURCE_IDENTIFIER.files/ |
114 | * store the filename in the blob property. | 136 | * store the filename in the blob property. |
115 | * delete the file when the corresponding entity is deleted. | 137 | * delete the file when the corresponding entity is deleted. |
116 | 138 | ||
@@ -132,7 +154,7 @@ Clients.. | |||
132 | 154 | ||
133 | Resources.. | 155 | Resources.. |
134 | 156 | ||
135 | * move the file to $DATADIR/storage/$RESOURCE_IDENTIFIER_files/ | 157 | * move the file to $DATADIR/storage/$RESOURCE_IDENTIFIER.files/ |
136 | * store the new path in the entity | 158 | * store the new path in the entity |
137 | 159 | ||
138 | #### Design Considerations | 160 | #### Design Considerations |
@@ -145,7 +167,7 @@ Using regular files as the interface has the advantages: | |||
145 | The copy is necessary to guarantee that the file remains for the client/resource even if the resource removes the file on it's side as part of a sync. | 167 | The copy is necessary to guarantee that the file remains for the client/resource even if the resource removes the file on it's side as part of a sync. |
146 | The copy could be optimized by using hardlinks, which is not a portable solution though. For some next-gen copy-on-write filesystems copying is a very cheap operation. | 168 | The copy could be optimized by using hardlinks, which is not a portable solution though. For some next-gen copy-on-write filesystems copying is a very cheap operation. |
147 | 169 | ||
148 | ### Database choice | 170 | ## Database choice |
149 | By design we're interested in key-value stores or perhaps document databases. This is because a fixed schema is not useful for this design, which makes | 171 | By design we're interested in key-value stores or perhaps document databases. This is because a fixed schema is not useful for this design, which makes |
150 | SQL not very useful (it would just be a very slow key-value store). While document databases would allow for indexes on certain properties (which is something we need), we did not yet find any contenders that looked like they would be useful for this system. | 172 | SQL not very useful (it would just be a very slow key-value store). While document databases would allow for indexes on certain properties (which is something we need), we did not yet find any contenders that looked like they would be useful for this system. |
151 | 173 | ||
@@ -216,9 +238,10 @@ Other useful properties: | |||
216 | * reading about 30% the speed of lmdb | 238 | * reading about 30% the speed of lmdb |
217 | * slow writes with transactions | 239 | * slow writes with transactions |
218 | 240 | ||
219 | ## Indexes | 241 | ### Result |
242 | LMDB was chosen as one of the few contenders that are embeddable and have true multi process support. It also outperformed unqllite significantly, although its write performance and disk usage aren't ideal. | ||
220 | 243 | ||
221 | ### Index Choice | 244 | ### Indexes |
222 | Additionally to the primary store, indexes are required for efficient lookups. | 245 | Additionally to the primary store, indexes are required for efficient lookups. |
223 | 246 | ||
224 | Since indexes always need to be updated they directly affect how fast we can write data. While reading only a subset of the available indexes is typically used, so a slow index doesn't affect all quries. | 247 | Since indexes always need to be updated they directly affect how fast we can write data. While reading only a subset of the available indexes is typically used, so a slow index doesn't affect all quries. |
@@ -245,6 +268,11 @@ Since indexes always need to be updated they directly affect how fast we can wri | |||
245 | * fast full text searching | 268 | * fast full text searching |
246 | * MVCC concurrency | 269 | * MVCC concurrency |
247 | 270 | ||
271 | ### Result | ||
272 | For regular secondary indexes LMDB is used as well, because it's sufficient for key lookups, and by using the same database, we can store the indexed data directly in the same transaction. | ||
273 | |||
274 | No solution for full-text indexes has been chosen yet. Baloo implements a fulltext index on top of LMDB though. | ||
275 | |||
248 | ## Useful Resources | 276 | ## Useful Resources |
249 | * LMDB | 277 | * LMDB |
250 | * Wikipedia for a good overview: <https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database> | 278 | * Wikipedia for a good overview: <https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database> |