konzept.txt 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  1. nodes: Index = LL_upper, Object = Id LL_lower
  2. node_tags_global: Index = (Key Value), Object = LL_upper Id
  3. node_tags_local: Index = (LL_upper_24 Key Value), Object = Id
  4. ways: Index = LL_upper, Object = Id Count (Node-Id)*
  5. way_tags_global: Index = (Key Value), Object = LL_upper Id
  6. way_tags_local: Index = (LL_upper_24 Key Value), Object = Id
  7. relations: Index = LL_upper, Object = Id Count (Type Id Role-Id)*
  8. relation_tags_global: Index = (Key Value), Object = LL_upper Id
  9. relation_tags_local: Index = (LL_upper_24 Key Value), Object = Id
  10. area-query
  11. bbox-query
  12. filter
  13. query
  14. recurse
  15. print
  16. import
  17. update
  18. ---
  19. Update-Abhängigkeiten:
  20. node_tags_global: Um alte Tags finden zu können, müssen wir diese zunächst auslesen. Passiert über node_tags_local
  21. node_tags_local: Um alte Tags finden zu können, müssen wir diese zunächst auslesen. Dies erfordert den alten LL_index der Node, also Lesen von nodes
  22. nodes: Für alten LL_index muss diese Datei sowieso zunächst gelesen werden. Alternativ: LL_index-Verzeichnis
  23. *_tags_global/_local: analog
  24. ways/relations: werden auch aus ihrem LL_index-Verzeichnis ausgerüstet
  25. Für die neuen LL-Indizes müssen allerdings die LL-Indizes der betroffenen Nodes bzw. Ways bekannt sein. (können aus dem aktualisierten LL_index gelesen werden)
  26. Update-Strategie:
  27. a) Nodes
  28. aa) Lese die Löschindizes der alten Nodes aus
  29. ab) Lösche und Aktualisiere die Nodes
  30. ac) Lese die Tags der alten Nodes ein
  31. ad) Lösche und Aktualisiere nodes_local
  32. ae) Lösche und Aktualisiere nodes_global
  33. b) Ways
  34. ba) Lese die Positionen der benutzten Nodes aus LL_index aus
  35. bb) Lese die Löschindizes der alten Ways aus
  36. bc) Lösche und Aktualisiere die Ways
  37. bd) Lese die Tags der alten Ways ein
  38. be) Lösche und Aktualisiere ways_local
  39. bf) Lösche und Aktualisiere ways_global
  40. c) Relations
  41. ca) Lese die Positionen der benutzten Nodes und Ways aus LL_index aus
  42. cb) Lese die Löschindizes der alten Relations aus
  43. cc) Lösche und Aktualisiere die Relations
  44. cd) Lese die Tags der alten Relations ein
  45. ce) Lösche und Aktualisiere relations_local
  46. cf) Lösche und Aktualisiere relations_global
  47. Node_Updater:
  48. to_delete_ids
  49. to_delete_idxs
  50. ---
  51. Welcher Index gehört wo hin?
  52. Sei ein Index q gegeben.
  53. a) Gibt es einen Blockindex b mit Index b' des Folgeblocks, so dass b\leq q < b' gilt, so gehört q in diesen Block.
  54. b) q gehört genau dann in einen Mehrfachblock, wenn q gleich dem Blockindex ist.
  55. c) Gehört der größte Blockindex a, der kleiner als q ist, zu einem Mehrfachblock, so gehört q in den Nachfolgeblock.
  56. d) Ist q größer als der größte Blockindex a und dieser ein Mehrfachblock, so gehört q in einen eigenen Block hinterher.
  57. e) Ist q kleiner als jeder Blockindex und der erste Block ist kein Mehrfachblock, so gehört q in den ersten Block.
  58. f) Ist q kleiner als jeder Blockindex und der erste Block ist ein Mehrfachblock, so gehört q in einen eigenen Block voraus.
  59. Anders formuliert:
  60. a) Es gibt "Segmentblöcke" (= mehrere Blöcke enthalten Daten zu nur einem Index) und "Gruppenblöcke" (= Nachfolge- und Vorgängerblock haben beide verschiedene Indizes).
  61. b) Suchen nach Blöcken (zum Lesen wie zum Einfügen) können daher leere Ergebnisse haben, einen oder mehrere Blöcke zurückliefern.
  62. c) Die Blockstruktur der Datenbank definiert eine Zerlegung des Indexraum wie folgt:
  63. - eine Familie von Segmentblöcken ist genau ihr Blockindex zugeordnet
  64. - hat ein Gruppenblock einen Gruppenblock als Vorgänger, so hat er als Untergrenze der Blockindizes seinen eigenen Startwert, sonst der minimale Index oder der Index, der infitesimal größer als der vorhergehende Blockindex ist
  65. - hat ein Gruppenblock einen Gruppenblock oder Segmentblock als Nachfolger, so hat er die Obergrenze infinitesimal kleiner als sein Nachfolgeblock, sonst das Supremum des Indexraums
  66. Pragmatisch:
  67. - wir wollen flach suchen (alles finden), mit einer diskreten Menge von Indizes suchen oder mit der Vereinigung diverser Intervalle suchen können.
  68. - Lesen wollen wir mit allen drei Methoden, schreiben nur mit diskreten Indizes (denn die zu schreibenden und zu löschenden Objekte haben ja Indizes).
  69. - der letzte Block eines Segmentblock-Familie muss beim Schreiben anders behandelt werden.
  70. - wir können keine infinitesimal größeren oder kleineren sowie keine Infima und Suprema sinnvoll generisch darstellen. Für das Lesen reicht es aber, sich auf tatsächlich in der Datenbank vorkommende Indizes zu beschränken, für das Schreiben reicht es, sich auf in der Schreibanfrage vorkommende Indizes zu beschränken.
  71. Flacher Iterator:
  72. block_type()
  73. ++, ==, =, (self), ~
  74. flat_begin(), flat_end()
  75. read_block(..)
  76. Diskreter Iterator:
  77. block_type()
  78. lower_bound(), upper_bound()
  79. ++, ==, =, (self), ~
  80. discrete_begin(..), discrete_end()
  81. read_block(..)
  82. insert_block(..)
  83. replace_block(..)
  84. Intervall-Iterator:
  85. block_type()
  86. lower_bound(), upper_bound()
  87. ++, ==, =, (self), ~
  88. range_begin(..), range_end()
  89. read_block(..)
  90. Die Semantik von ++, ==, =, (self), ~, *_end() und flat_begin() ist die übliche.
  91. discrete_begin(..) nimmt ein Paar von Iteratoren über einen Container mit Indizes entgegen und iteriert über genau die Blöcke, die für diese Menge von Iteratoren relevant sein können.
  92. range_begin(..) tut das analoge für ein Paar von Iteratoren über einen Container von Intervallen.
  93. block_type() liefert einen der Werte \{ EMPTY, BLOCK, PARTIAL, LAST_PART \}
  94. read_block(..) ist eine Methode von File_Blocks, erhält als Argument einen Iterator und liefert den zugehörigen Datenblock aus.
  95. insert_block(..) ist eine Methode von File_Blocks, erhält als Argument einen diskreten Iterator und fügt den Block unmittelbar vor dem Block ein.
  96. replace_block(..) ist eine Methode von File_Blocks, erhält als Argument einen diskreten Iterator und ersetzt den vom Iterator bezeichneten Block.
  97. lower_bound() liefert die jeweils sinnvolle Untergrenze: für einen diskreten Iterator ist das der kleinste Index aus der Anfragemenge, der zum aktuellen Block des Iterators gehört. Für einen Intervall-Iterator ist dies der kleinste Index, der zum Block gehört und tatsächlich in der Datenbank existiert.
  98. upper_bound() liefert den kleinsten Index, der schon außerhalb des Blocks liegt. Für einen diskreten Iterator ist dies der kleinste Index aus der Anfragemenge, für den dies zutrifft und ggf. ==end(). Bei Intervallen ist es der erste Index des nächsten Blocks oder ==end() im Falle des letzten Blocks in der Datenbank.
  99. 0, f96, 260a, 3276, 348c, 34ce
  100. void update(const map< TIndex, map< TObject > >& to_delete,
  101. const map< TIndex, map< TObject > >& to_insert);
  102. update: 3 Fälle
  103. a) leerer Bestand
  104. b) 1 Block Bestand, ggf. mehrere Blöcke neu
  105. c) mehrere Blöcke Bestand
  106. a)+b) erfordern Algorithmus zur Blockteilung:
  107. - Verwende stets höchstens so viele Blöcke wie nötig, verteile so gleichmäßig wie möglich.
  108. - Wenn zwei aufeinanderfolgende Indizes nicht zusammen in einen Block passen, muss dazwischen geteilt werden.
  109. - Minimal mögliche Anzahl an Blöcken kann per Greedy ermittelt werden.
  110. - Heuristik: fülle von hinten nach vorne jeweils bis zum Durchschnitt auf
  111. calc_split_idxs(vector< TIndex >& split, const map< TIndex, int32 >& sizes);
  112. benötigt: TObject.sizeof()
  113. Größenberechnung: Tindex.sizeof() + sizeof(uint32) + \sum TObject.sizeof()
  114. bei a)
  115. - berechne die Indexgrößen allein aus den Neuzugängen
  116. - schreibe alle Blöcke mittels insert_block(end)
  117. bei b)
  118. - berücksichtigte Bestandsdaten, Löschungen und Neuzugänge für Indexgrößen
  119. - achte auf Indizes, die nicht mehr existieren
  120. - schreibe alle bis auf den letzten Block mittels insert_block(it), dann replace_block(it)
  121. bei c)
  122. - fülle Blöcke mit Löschungen mit Neudaten auf, sofern möglich
  123. - fülle den letzten Block auf, also replace_block bzw. insert_block
  124. - wenn etwas übrig bleibt, hänge einen Block an, also replace_block und insert_block davor
  125. ---
  126. zum Thema Discrete_Iterator, hier: Verifikation von operator++ und Konstruktor
  127. Konstruktor:
  128. Wenn Indizes leer, dann Ende
  129. Wenn leer, dann leer zurück [ index_upper = (end) ]
  130. Wenn Block Segment ist und Index < Block.Index
  131. is_empty = true [ index_upper der erste Index, der >= Block.Index ist ]
  132. Wenn (Nachfolger != (end) && Nachfolger.Index <= erster Index)
  133. {
  134. Wenn (Index == erster Index)
  135. Segment (einzige Möglichkeit für Segment): liefere dieses zurück [ index_upper = index + 1 ]
  136. ++Block, ++Nachfolger
  137. }
  138. (*)
  139. Wenn (Nachfolger == (end))
  140. {
  141. (relevantes Segment kann nicht passieren, da Segmente mehrteilig sind)
  142. wenn Last_Segment, dann liefere dahinter (leer) zurück [ index_upper = (end) ]
  143. ansonsten liefere diesen Block zurück [ index_upper = (end) ]
  144. }
  145. Wenn (Block ein Last_Segment ist)
  146. is_empty = true [ index_upper der erste Index, der >= Block.Index ist ]
  147. liefere Block zurück [ index_upper der erste Index, der >= Nachfolger.Index ist ]
  148. Wohldef: ok
  149. Terminierung: ok, da endlich viele Blöcke
  150. Korrektheit:
  151. - richtiger Block: nur erster Index relevant. Korrekt, falls er auf ein Segment passt. An (*) gilt, dass (Nachfolger == (end) || (Nachfolger.Index > erster Index)), also wird auf keinen Fall ein zur kleiner Block zurückgeliefert. Außerdem gilt für keinen Block vor Nachfolger, dass (Index > erster Index). Also richtiger Block.
  152. - index_upper: für Segments korrekt per Definition, für Groups per Konstruktion
  153. ---
  154. operator++:
  155. mögliche Zustände:
  156. - group
  157. - segment
  158. - last_segment
  159. - is_inserted
  160. - is_empty (bezieht sich auf den Folgeblock wenn is_inserted gesetzt ist)
  161. Wenn (is_inserted)
  162. liefere ++Block zurück [ index, is_empty unverändert ]
  163. Wenn (is_empty)
  164. setze (is_empty) zurück
  165. setze index_lower = index_upper und index_upper:
  166. falls Block == (end) auf (end)
  167. ansonsten [ index_upper der erste Index, der >= Block.Index ist ]
  168. fertig
  169. Wenn Segment
  170. liefere ++Block zurück, Indices unverändert
  171. (*)
  172. index_lower = index_upper
  173. ++Block
  174. Wenn (Block == (end))
  175. fertig
  176. Wenn Block Segment ist und Index < Block.Index
  177. is_empty = true [ index_upper der erste Index, der >= Block.Index ist ]
  178. Wenn (Nachfolger != (end) && Nachfolger.Index <= erster Index)
  179. {
  180. Wenn (Index == erster Index)
  181. Segment (einzige Möglichkeit für Segment): liefere dieses zurück [ index_upper = index + 1 ]
  182. ++Block, ++Nachfolger
  183. }
  184. Wenn (Nachfolger == (end))
  185. {
  186. (relevantes Segment kann nicht passieren, da Segmente mehrteilig sind)
  187. wenn Last_Segment, dann liefere dahinter (leer) zurück [ index_upper = (end) ]
  188. ansonsten liefere diesen Block zurück [ index_upper = (end) ]
  189. }
  190. Wenn (Block ein Last_Segment ist)
  191. is_empty = true [ index_upper der erste Index, der >= Block.Index ist ]
  192. liefere Block zurück [ index_upper der erste Index, der >= Nachfolger.Index ist ]
  193. Ab (*) gilt, dass mit dem Ende von Block alle Indices mit < index_upper abgearbeitet sind.
  194. Wohldef: ok
  195. Terminierung: ok, da endlich viele Blöcke
  196. Korrektheit:
  197. - richtiger Block: Im Normalfall wird (*) erreicht, dann siehe oben. Für Segments wird korrekt der Folgeblock zurückgeliefert. is_empty und is_inserted werden ebenso explizit behandelt
  198. - index_lower: s. index_upper
  199. - index_upper: Im Normalfall wird (*) erreicht, dann siehe oben. Segments, is_empty und is_inserted werden explizit behandelt.
  200. ---
  201. insert
  202. Zusicherung:
  203. - es gilt is_writeable (Ausnahme!)
  204. - es ist nicht is_inserted gesetzt (Ausnahme!)
  205. - es werden nur Indices eingetragen, die >= index_lower und < index_upper sind
  206. - für jeden Index in diesem Block gilt, dass er kleiner als Block.Index (des Blocks unter dem Iterator) wird, nachdem der Block bearbeitet ist.
  207. Block wird auf den neuen Block gesetzt und dessen Index in die Blockliste aufgenommen; es wird is_inserted gesetzt. Achtung: is_empty bezieht sich (wie die Indexgrenzen) nun auf den Folgeblock.
  208. ---
  209. replace
  210. Zusicherung:
  211. - es gilt is_writeable (Ausnahme!)
  212. - es gilt nicht is_empty (Ausnahme!)
  213. - es werden nur Indices und alle verbleibenden Indices eingetragen, die >= index_lower und < index_upper sind
  214. Durch die Zusicherung ist sichergestellt, dass sich dies so mit operator++ verträgt.
  215. ---
  216. replace delete
  217. Zusicherung:
  218. - es gilt is_writeable (Ausnahme!)
  219. - es gilt nicht is_empty (Ausnahme!)
  220. Es wird zum nächsten Block gewechselt. Durch die Zusicherung ist sichergestellt, dass sich dies so mit operator++ verträgt.
  221. ---
  222. The concept and why it is safe: In general, there may be at most one write process, but an arbitrary number of read processes.
  223. The assertion for writing is that at any time, the disk is in a well defined state. The allowed to states are:
  224. (1) No file named "dirty" exists. Then the idx files for all files are consistent with the data files. If an idl file exists, it marks the unused blocks. Otherwise every not referenced block is defined to be unused.
  225. (2) A file named "dirty" exists. Then the idy files for all files are consistent with the data files and every not referenced block is defined to be unused.
  226. The system now writes new content into unused or extra blocks and the updates index into an idy file instead of the idx file. It will succeed in a state where idx and idx files both point consistently into (possible different blocks of) the files. Thus, a switch from (1) to (2) is immediately possible. In state (2), the dispatcher copies from the idy files to the idx files. Once it is finished, idx and idx files have equal content, and the system can switch by removing "dirty" immediately back to (1).
  227. The assertion for the interplay of reading and writing are more complex: A reading process first has the oppurtunity to make its copy of the idx files. These remain unchanged while the reading process has a reading_idx lock. Then, the reading process has the assertion that all blocks referenced from this idx remain available and unchanged until it releases its read lock. For this purpose, these blocks must not appear in an idl file. Every block that is neither registered in such an index nor the curent index shall appear in the idl file.
  228. ---
  229. Automated Testing Environment:
  230. This is realized with a bash script, located in osm-3s_testing.
  231. A test is a line $EXEC $I $ARGS where $ARGS collect all arguments, but the first is the number of the test within $EXEC, plus a directory osm-3s_testing/expected/$EXEC_$I which contains the expected output. osm-3s_testing/input/$EXEC_$I contains the input for the test. The test is then executed by wiping the directory osm-3s_testing/run/$EXEC_$I, then copying the input there. The test is run as test-bin/$EXEC $I $ARGS. Then, the output is diffed file by file to expected. If any differences exist, it returns FAILED, otherwise "passed" and deletes run/$EXEC_$I. The standard input is copied to stdout.log, standard error to stderr.log.
  232. Directory structure:
  233. bin
  234. build
  235. cgi-bin
  236. html
  237. src
  238. +-bin
  239. +-cgi-bin
  240. +-expat
  241. +-overpass_api
  242. | +-core
  243. | +-dispatch
  244. | +-frontend
  245. | +-osm-backend
  246. | +-statements
  247. +-public_transport
  248. +-template_db
  249. +-test-bin
  250. +-utils
  251. test_data
  252. +-overpass_api
  253. | +-...
  254. +-template_db
  255. +-...
  256. test-bin
  257. ---
  258. Transactions::Write
  259. /* Takes the write mutex of the database by writing its pid into the file
  260. ".lock". It waits for a second whether its pid is still present to avoid
  261. race conditions. */
  262. void write_lock(pid_t pid) throws File_Error;
  263. /* Tests:
  264. Call with nonexisting file: should succeed.
  265. Call with empty file: should succeed.
  266. Call with filled file: should throw File_Error.
  267. Call concurrent with writing process: should throw File_Error.
  268. */
  269. /* Retrieves the current list of empty blocks. It triggers the generation of
  270. an idl file by the dispatcher, then reads this file. */
  271. vector< vector< uint > > collect_empty_blocks
  272. (pid_t pid, string dispatcher_share_name, string idl_filename)
  273. throws Dispatcher_Error, File_Error;
  274. /* To dispatcher: sends Dispatcher::REQUEST_IDL and its pid to the dispatcher.
  275. Waits 10x10ms for response, then resends Dispatcher::REQUEST_IDL if no answer is given.
  276. The expected response is the pid. */
  277. /* Tests:
  278. Fast responding vs. slow responding dispatcher: should wait for response.
  279. No dispatcher: should throw message.
  280. Check dispatcher communication.
  281. Empty file, file covering one or several files and containing one or several
  282. blocks for each file: should reproduce exactly this list. */
  283. /* Testen, dass neu nach idy geschrieben wird. */
  284. /* Releases the mutex without moving any index file. */
  285. void write_rollback(pid_t pid) throws File_Error;
  286. /* Tests:
  287. Call with nonexisting file: should throw File_Error.
  288. Call with a file: should succeed and remove the file.
  289. */
  290. /* Triggers the dispatcher to copy from idy to idx. Releases the write mutex
  291. afterwards. */
  292. void write_commit(pid_t pid, string dispatcher_share_name) throws File_Error, Dispatcher_Error;
  293. /* To dispatcher: sends Dispatcher::WRITE_COMMIT and its pid to the dispatcher.
  294. Waits 10x10ms for response, then resends Dispatcher::WRITE_COMMIT if no answer is given.
  295. The expected response is the pid. */
  296. /* Tests:
  297. Fast responding vs. slow responding dispatcher: should wait for response.
  298. No dispatcher: should throw message.
  299. Check dispatcher communication.
  300. Call with nonexisting file: should throw File_Error.
  301. Call with a file: should succeed and remove the file.
  302. */
  303. Transactions::Read
  304. /* Retrieves all index files. Registers at the dispatcher and locks during the
  305. file retrieval. */
  306. void read_subscribe(pid_t pid, string dispatcher_share_name, string idx_share_name) throws File_Error, Dispatcher_Error;
  307. /* To dispatcher: sends Dispatcher::REQUEST_READ_AND_IDX and its pid.
  308. Waits 10x10ms for response, then resends Dispatcher::REQUEST_READ_AND_IDX if no
  309. answer is given. The expected response is the pid.
  310. Then it attemps to read the given share. If that fails, reads the given idx files.
  311. Afterwards sends Dispatcher::IDX_READ_DONE to dispatcher.
  312. Waits 10x10ms for response, then resends Dispatcher::IDX_READ_DONE if no
  313. answer is given. The expected response is the pid. */
  314. /* Tests:
  315. Fast responding vs. slow responding dispatcher: should wait for response both times.
  316. No dispatcher: should throw message.
  317. Check dispatcher communication.
  318. Read from empty share.
  319. Read from populated share.
  320. Read from usual idx files, empty and populated.
  321. */
  322. /* Releases the read lock. */
  323. void read_quit(pid_t pid, string dispatcher_share) throws Dispatcher_Error;
  324. /* To dispatcher: sends Dispatcher::READ_FINISHED and its pid.
  325. Waits 10x10ms for response, then resends Dispatcher::READ_FINISHED if no
  326. answer is given. The expected response is the pid. */
  327. /* Tests:
  328. Fast responding vs. slow responding dispatcher: should wait for response.
  329. No dispatcher: should throw message.
  330. Check dispatcher communication.
  331. */
  332. State of the file system:
  333. - *.map, *.bin files for the data
  334. - *.idx always
  335. - *.lock and possibly *.idl and *.idy during a write action
  336. - *.idy and dirty if the *.idx files are rewritten
  337. and
  338. - a bidirectional shared memory.
  339. - a shared memory managed by dispatcher to pass the idx files (postponed).
  340. /* Waits in its main loop for one of the following requests. Adtionally, it
  341. checks from time to time if the registered readers still exist.*/
  342. Transactions::Dispatcher
  343. {
  344. class Idx_Footprints
  345. {
  346. void set_current_footprint(vector< vector< bool > >);
  347. void register(pid_t pid);
  348. void unregister(pid_t pid);
  349. vector< pid_t > registered_processes() const;
  350. vector< vector< bool > > total_footprint() const;
  351. };
  352. /* Opens a shared memory for dispatcher communication. Furthermore,
  353. detects whether idx or idy are valid, clears to idx if necessary,
  354. and loads them into the shared memory idx_share_name. */
  355. Dispatcher::Dispatcher(string dispatcher_name, string idx_share_name,
  356. vector< File > managed_files) throws File_Error, Dispatcher_Error;
  357. /* Tests:
  358. Shall throw an error if one of the shares is not accessible.
  359. Shall throw an error if a file in the db_dir is not accessible.
  360. Shall read all idy if dirty is present, all idx files otherwise. For this
  361. purpose, the content of the shared memory can be checked against the idx files.
  362. Read a set of idy files when dirty is present.
  363. Read a set of idx files when dirty is absent.
  364. */
  365. /* Changes the state of the process identified by its pid from reading_idx
  366. to reading the files. */
  367. void Dispatcher::idx_read_done(pid_t pid);
  368. /* See tests below. */
  369. /* Unregisters the process identified by its pid from reading the files. */
  370. void Dispatcher::read_finished(pid_t pid);
  371. /* See tests below. */
  372. /* Writes the current total footprint into an idl file. It doesn't need
  373. a mutex because it will include the current index anyway. The worst thing
  374. possible, resulting from a race condition with an unregistering read
  375. process would be some blocks that are kept unnecessarily reserved. */
  376. void Dispatcher::request_idl(pid_t pid) const;
  377. /* See tests below. */
  378. /* Registers the process identified by its pid for reading the idx share. */
  379. void request_read_and_idx(pid_t pid);
  380. /* See tests below. */
  381. /* Validates the idy files. Then it copies the idy files to the idx files
  382. and to the idx share. Afterwards, it revalidates the idx files. */
  383. void Dispatcher::write_commit(pid_t pid);
  384. /* See tests below. */
  385. /* Checks whether all read processes still exist and removes no longer
  386. existing processes from reading_idx_pids and footprints. */
  387. void Dispatcher::purge_zombies();
  388. /* See tests below. */
  389. /* If pending_commit is false, all commands will be processed. If pending
  390. commit is true, request_read_and_idx is blocked. Write_commit is only
  391. possible if reading_idx_pids is empty, otherwise only pending_commit will
  392. be activated. */
  393. void main_loop();
  394. /* Tests for the mutexes:
  395. write_commit with no running reading process: should return success.
  396. Register a process for reading_idx; wait for response.
  397. write_commit with a process in mode reading_idx: should not return.
  398. Register the process for reading the files; wait for response.
  399. write_commit with no running reading process: should return success.
  400. Register a second and third process for reading_idx; wait for response.
  401. write_commit with a process in mode reading_idx: should not return.
  402. Register the third process for reading the files; wait for response.
  403. Register a fourth and fifth process for reading_idx; wait for response.
  404. Register the fourth then the second process for reading the files; wait for response.
  405. write_commit with a process in mode reading_idx: should not return.
  406. Register the fifth process for reading the files; wait for response.
  407. write_commit with no running reading process: should return success.
  408. Unregister all processes.
  409. Test for the idl content:
  410. Fill Dispatcher with a simple idx file, e.g. ((0 1)), ((0 1), (0 3)).
  411. Request idl file.
  412. Register a process for reading_idx; wait for response.
  413. Request idl file.
  414. Register a second process for reading_idx; wait for response.
  415. Request idl file.
  416. Register both processes for reading the files; wait for response.
  417. Request idl file.
  418. Fill Dispatcher with a simple idy file, e.g. ((0 2)), (); call write_commit.
  419. Request idl file.
  420. Fill Dispatcher with a simple idy file, e.g. ((0 3)), ((0 5)); call write_commit.
  421. Request idl file.
  422. Register a process for reading_idx; wait for response.
  423. Request idl file.
  424. Fill Dispatcher with a simple idy file, e.g. ((0 4)), ((0 2)); call write_commit.
  425. Request idl file.
  426. Unregister the third process.
  427. Request idl file.
  428. Unregister the first process.
  429. Request idl file.
  430. Unregister the second process.
  431. Request idl file. Now, only the last idy file should remain.
  432. Test for correct idl and idx generation:
  433. Fill Dispatcher with idy file ((0 1), (0 2)) but 3 blocks.
  434. Request idl file. Check idx files.
  435. Fill Dispatcher again. Request idl file.
  436. Fill Dispatcher with idy file ((0 2)) but 3 blocks.
  437. Request idl file. Check idx files.
  438. Fill Dispatcher with idy file ((0 1), (0 2), (0 3)) and 3 blocks.
  439. Request idl file. Check idx files.
  440. */
  441. Idx_Footprints footprints;
  442. vector< vector< bool > > current_footprint;
  443. vector< int > reading_idx_pids;
  444. bool pending_commit;
  445. };
  446. ---
  447. Smart pointer:
  448. (..) : delegate to new Pimpl(..), set counter=1
  449. ~(..) : decrement counter, call destructor if applicable
  450. (&) : copy pointer, increment counter
  451. = : if !=, increment new one, decrement old one, call destrcutor if applicable
  452. Read_Without: Index from idx + File_Ext for all on construction, Void_Index empty, no write back. Constructor: (vec< File_Prop* >, string file_ext, bool writeable, bool shadow)
  453. Write_Without: Index from idx + File_Ext for all on construction, Void_Index by calculation, write back.
  454. Read_Transact: Index from Share on construction, Void_Index empty, no write back.
  455. Write_Transact: Index from Shadow on construction, Void_Index empty, write back to shadow.
  456. get_file_name_extension()
  457. get_index()
  458. ---
  459. query:
  460. - Read all elem-ids from one-block-clauses.
  461. - Intersect them if applicable.
  462. - If there are less than THRESHOLD, locate them. (cache?)
  463. - Could be faster if the index is stored along with the node_id. To reduce the data amount, we could attach a shortened index to the key. Does this pay off? We need at least 24 bit of index to make meaningful predictions. This will be 150000 to 750000 indices, thus useful only for tags with more than 1.2 mio. to 5 mio. occurences. If you query for such a tag alone, you will exceed the element limit anyway.
  464. - Small area/bbox works like a query with few results and known locations, but intersecting isn't possible.
  465. - if known, indices are provided. World assumed otherwise.
  466. - prelocated searches are performed via local_tags, one located clause suffices.
  467. area-query/bbox-query:
  468. - see above
  469. recurse:
  470. - Indices can be translated one by one. This isn't helpful if the initial index of a way or relation takes much space.
  471. - Element numbers could be calculated from the idx files, assuming an uniform distribution. Once again, this profits from more precise way indices.
  472. union/intersect:
  473. - Indices are unified/intersected.
  474. - Element numbers go to proportionals of the shared indices, based on the averages of all sets. The union has at most the sum of all counts, the intersection at most the minimum of all counts.
  475. print:
  476. - only output.
  477. All times need to be mesured with different samples.
  478. ---
  479. Our current concept of Random_File is incompatible to transactionality, basically because there is only one valid position in the file for each entry.
  480. Possible redesigns of Random_File:
  481. Things to benchmark:
  482. a Number of necessary disk seeks.
  483. b Amount of initial data to load for an index up to 4GB. Can be cached in RAM later on if it isn't too much.
  484. c Disk overhead for no changes.
  485. d Disk overhead for sparse changes, but lots of versions.
  486. e Implementation effort.
  487. 1. Multiversion B-Tree
  488. a. Needs a logarithmic number of disk seeks, probably the worst of all concepts, but at least it scales acceptably.
  489. b. 4 byte.
  490. c. Saved indexes. Depens on the size of the B-tree, but at least 256KB (the largest level above the indices themselves dominates.)
  491. d. Every change triggers copying the underlying block, thus one block per change and version.
  492. e. Get the concept of B-trees in details, then re-estimate. Looks like a recursion can help, but I expect caveats.
  493. 2. Multiversion Single Level Index file
  494. a. Needs no extra disk seeds, provided, the index is loaded at intialization into RAM.
  495. b. 256 KB with blocks of 64KB.
  496. c. 256 KB with blocks of 64KB.
  497. d. Every change triggers copying the underlying block, thus one block of 256KB per change and version.
  498. e. Similar to the approach for block backend - the results can be reused immediately there.
  499. 3. Conversion into Block_Backend
  500. a. Needs no extra disk seeds.
  501. b. Derived on the same basis as 2., but threefold due to extra data.
  502. c. If indexes are used as keys, the file is three times bigger than the payload. If some hash of the index is used, fewer waste, but we need to reassemble each value. It then is a worse version of 2.
  503. d. Every change triggers copying the underlying block, thus one block per change and version.
  504. e. Straightforward.
  505. 4. Journal of Changes with incremental change records.
  506. a. One disk seek for every active version, but one with loading the entire diff file.
  507. b. 8 bytes.
  508. c. No overhead.
  509. d. Few bytes per change.
  510. e. It's an interesting, but challeging task: a lot of questions on the incremental still need to be answered, and there will be anyway two completely different storage formats, the map file and the incremental files.
  511. 5. Journal of Changes with total change record.
  512. a. Exactly two disk seeks, but one with loading the entire diff file.
  513. b. 8 bytes.
  514. c. No overhead.
  515. d. Few bytes per change.
  516. e. Similar to 4.
  517. 5. Hash table of Changes with total change record.
  518. a. Exactly two disk seeks, but one with loading potentially more data.
  519. b. either 8 byte or the entire hash table.
  520. c. The hash table, size e.g. 256 KB.
  521. d. The hash table, size e.g. 256 KB.
  522. e. Similar to 4, but even more challenging.
  523. Decision: 3. is too inefficient. 2. has similar implementation effort (the effort can be reused later), but an acceptable storage performance. In particular, it has no overhead at read time with a preloaded index. Everything else has significant risks that the implementation gets too complex. Take approach 2. and keep it modular to improve it later if necessary.
  524. ---
  525. Dedicated functions with the following signature:
  526. uint32 join_index_of(begin, end< uint32 >)
  527. vector< uint32 > indices_to_upper(begin, end< uint32 >)
  528. vector< uint32 > indices_to_upper(Range_begin, Range_end)
  529. vector< uint32 > indices_to_lower(begin, end< uint32 >)
  530. vector< uint32 > indices_to_lower(Range_begin, Range_end)
  531. Tasks:
  532. for 0.6.1: (planned 2011-03-18)
  533. + clear file requirements for areas
  534. + introduce cgi_query and console_query --verbose
  535. + rewrite progress messages
  536. + repair update_moved_idxs
  537. = reorganize project dirs
  538. + rework bbox-query: should use fewer ranges
  539. + automated test environment
  540. Released: 2011-04-06
  541. ===database changes===
  542. This version is binary compatible to the previous version 0.6.
  543. ===interface changes===
  544. The log levels of osm3s_query have been reworked. I hope the default log level now contains sufficient comprehensible and only comprehensible messages. The progress information of update_database shall now be rather self-explanatory than diagnostic.
  545. ===other changes===
  546. With the advent of a comprehensive automated testbed, a couple of bugs have been revealed and fixed:
  547. - The print statement doesn't throw anymore an error on absent optional parts of the database.
  548. - update_database can now properly apply diff files. It didn't update the indexes of ways and relations when only the underlying nodes have moved before.
  549. - bbox_query doesn't anymore crash on very large bounding boxes.
  550. - Relations that have only other relations as members have been accidently unchangeable in the previous version.
  551. - A possible buffer overflow in the database backend has been fixed.
  552. - Two other, rather arcane bugs in the database fixed.
  553. for 0.7 (planned 2011-04-15)
  554. = refactor file_blocks.h: reorganize file(s)
  555. = refactor block_backend.h: reorganize file(s)
  556. = refactor and test osm_backend_updater
  557. + automate dispatcher test
  558. = divide mirrored and derived database
  559. @database:
  560. + changes for transactionality: different idx management
  561. - reworked way and relation indices
  562. - extend values in tags_global
  563. for 0.7.1: (planned 2011-04-22)
  564. - polygon-query
  565. = remaining automated test environment
  566. for 0.8: (planned 2011-05-13)
  567. = make rules possible
  568. for 0.8.1: (planned 2011-06-03)
  569. - refactor file_blocks.h: spin Write_Iterator off Discrete_Iterator
  570. - refactor file_blocks.h: write rough documentation
  571. - speed optimizations for areas
  572. - XAPI interface
  573. for 0.8.2: (planned 2011-06-17)
  574. - use automake make targets (at least "check")
  575. for 0.9: (planned 2011-07-01)
  576. - version, user and time data
  577. ---
  578. Security considerations:
  579. We consider any attack from outside that would make the server using excessive resources or executing malicious code. We expect a possible attack to fall into one of the following categories:
  580. (1) Sending ill formed input that makes the server execute machine code.
  581. (2) Sending input that is executed as script but works as malware at that level.
  582. (3) Sending input that causes excessive load.
  583. (4) Sending input to figure out server configuration data.
  584. The server is hardened against (1): Data is converted from Expat straight to C++ strings and doubles; thus there is little risk. (2) is avoided by design: the script language is intentionally not Turing complete, and it allows anyway not more than printing a subset of the mirrored OSM database.
  585. (3) is not completely addressed at the moment: An attacker may put so much queries from outside that the server runs out of memory. The individual queries are limited in size (to 512 MB of RAM), but not their total number. The other resource problem, queries that get runaway for a long time, are addressed: Every query is annotated with a timeout (180s or a user given) and terminated if it surpasses its time limit.
  586. (4) is not a concern at this state of the project: The server may give file names of the database or the containing directory which is useful to debug a server after installation.