droonga-engine 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. data/.dir-locals.el +3 -0
  2. data/.gitignore +6 -0
  3. data/.travis.yml +15 -0
  4. data/.yardopts +7 -0
  5. data/Gemfile +66 -0
  6. data/LICENSE.txt +14 -0
  7. data/README.md +17 -0
  8. data/Rakefile +64 -0
  9. data/benchmark/benchmark.rb +123 -0
  10. data/benchmark/utils.rb +246 -0
  11. data/benchmark/watch/benchmark-notify.rb +143 -0
  12. data/benchmark/watch/benchmark-notify.sh +20 -0
  13. data/benchmark/watch/benchmark-publish.rb +120 -0
  14. data/benchmark/watch/benchmark-scan.rb +213 -0
  15. data/bin/droonga-catalog-generate +103 -0
  16. data/bin/droonga-engine +20 -0
  17. data/bin/droonga-engine-service +20 -0
  18. data/doc/text/news.md +106 -0
  19. data/droonga-engine.gemspec +52 -0
  20. data/lib/droonga/adapter.rb +48 -0
  21. data/lib/droonga/adapter_runner.rb +104 -0
  22. data/lib/droonga/catalog/base.rb +41 -0
  23. data/lib/droonga/catalog/collection_volume.rb +106 -0
  24. data/lib/droonga/catalog/dataset.rb +69 -0
  25. data/lib/droonga/catalog/errors.rb +113 -0
  26. data/lib/droonga/catalog/schema.rb +186 -0
  27. data/lib/droonga/catalog/single_volume.rb +28 -0
  28. data/lib/droonga/catalog/slice.rb +41 -0
  29. data/lib/droonga/catalog/version1.rb +427 -0
  30. data/lib/droonga/catalog/version2.rb +96 -0
  31. data/lib/droonga/catalog/version2_validator.rb +63 -0
  32. data/lib/droonga/catalog/volume.rb +33 -0
  33. data/lib/droonga/catalog/volume_collection.rb +56 -0
  34. data/lib/droonga/catalog_generator.rb +156 -0
  35. data/lib/droonga/catalog_loader.rb +56 -0
  36. data/lib/droonga/catalog_observer.rb +83 -0
  37. data/lib/droonga/collector.rb +38 -0
  38. data/lib/droonga/collector_message.rb +71 -0
  39. data/lib/droonga/collector_runner.rb +64 -0
  40. data/lib/droonga/collectors/and.rb +26 -0
  41. data/lib/droonga/collectors/or.rb +26 -0
  42. data/lib/droonga/collectors/sum.rb +26 -0
  43. data/lib/droonga/collectors.rb +18 -0
  44. data/lib/droonga/dispatcher.rb +326 -0
  45. data/lib/droonga/distributed_command_planner.rb +179 -0
  46. data/lib/droonga/distributor.rb +87 -0
  47. data/lib/droonga/engine/command/droonga_engine.rb +441 -0
  48. data/lib/droonga/engine/version.rb +20 -0
  49. data/lib/droonga/engine.rb +80 -0
  50. data/lib/droonga/engine_state.rb +79 -0
  51. data/lib/droonga/error.rb +73 -0
  52. data/lib/droonga/error_messages.rb +33 -0
  53. data/lib/droonga/event_loop.rb +46 -0
  54. data/lib/droonga/farm.rb +58 -0
  55. data/lib/droonga/fluent_message_receiver.rb +191 -0
  56. data/lib/droonga/fluent_message_sender.rb +140 -0
  57. data/lib/droonga/forwarder.rb +119 -0
  58. data/lib/droonga/handler.rb +49 -0
  59. data/lib/droonga/handler_message.rb +61 -0
  60. data/lib/droonga/handler_messenger.rb +119 -0
  61. data/lib/droonga/handler_runner.rb +125 -0
  62. data/lib/droonga/input_message.rb +51 -0
  63. data/lib/droonga/job_protocol.rb +20 -0
  64. data/lib/droonga/job_pusher.rb +179 -0
  65. data/lib/droonga/job_receiver.rb +70 -0
  66. data/lib/droonga/loggable.rb +29 -0
  67. data/lib/droonga/logger.rb +142 -0
  68. data/lib/droonga/message_matcher.rb +109 -0
  69. data/lib/droonga/output_message.rb +55 -0
  70. data/lib/droonga/planner.rb +47 -0
  71. data/lib/droonga/pluggable.rb +31 -0
  72. data/lib/droonga/plugin/metadata/adapter_input_message.rb +39 -0
  73. data/lib/droonga/plugin/metadata/adapter_output_message.rb +39 -0
  74. data/lib/droonga/plugin/metadata/collector_message.rb +39 -0
  75. data/lib/droonga/plugin/metadata/handler_action.rb +39 -0
  76. data/lib/droonga/plugin/metadata/input_message.rb +54 -0
  77. data/lib/droonga/plugin.rb +43 -0
  78. data/lib/droonga/plugin_loader.rb +63 -0
  79. data/lib/droonga/plugin_registry.rb +66 -0
  80. data/lib/droonga/plugins/basic.rb +54 -0
  81. data/lib/droonga/plugins/crud.rb +145 -0
  82. data/lib/droonga/plugins/dump.rb +97 -0
  83. data/lib/droonga/plugins/error.rb +51 -0
  84. data/lib/droonga/plugins/groonga/column_create.rb +123 -0
  85. data/lib/droonga/plugins/groonga/column_list.rb +124 -0
  86. data/lib/droonga/plugins/groonga/column_remove.rb +65 -0
  87. data/lib/droonga/plugins/groonga/column_rename.rb +67 -0
  88. data/lib/droonga/plugins/groonga/delete.rb +117 -0
  89. data/lib/droonga/plugins/groonga/generic_command.rb +105 -0
  90. data/lib/droonga/plugins/groonga/generic_response.rb +43 -0
  91. data/lib/droonga/plugins/groonga/select.rb +236 -0
  92. data/lib/droonga/plugins/groonga/table_create.rb +111 -0
  93. data/lib/droonga/plugins/groonga/table_list.rb +120 -0
  94. data/lib/droonga/plugins/groonga/table_remove.rb +57 -0
  95. data/lib/droonga/plugins/groonga.rb +37 -0
  96. data/lib/droonga/plugins/search/distributed_search_planner.rb +407 -0
  97. data/lib/droonga/plugins/search.rb +146 -0
  98. data/lib/droonga/plugins/watch.rb +178 -0
  99. data/lib/droonga/processor.rb +63 -0
  100. data/lib/droonga/reducer.rb +169 -0
  101. data/lib/droonga/replier.rb +49 -0
  102. data/lib/droonga/schema_applier.rb +167 -0
  103. data/lib/droonga/searcher/mecab_filter.rb +67 -0
  104. data/lib/droonga/searcher.rb +733 -0
  105. data/lib/droonga/server.rb +45 -0
  106. data/lib/droonga/session.rb +99 -0
  107. data/lib/droonga/single_step.rb +68 -0
  108. data/lib/droonga/single_step_definition.rb +54 -0
  109. data/lib/droonga/slice.rb +122 -0
  110. data/lib/droonga/status_code.rb +25 -0
  111. data/lib/droonga/step_runner.rb +64 -0
  112. data/lib/droonga/sweeper.rb +42 -0
  113. data/lib/droonga/test/stub_handler.rb +37 -0
  114. data/lib/droonga/test/stub_handler_message.rb +35 -0
  115. data/lib/droonga/test/stub_handler_messenger.rb +34 -0
  116. data/lib/droonga/test/stub_planner.rb +31 -0
  117. data/lib/droonga/test.rb +21 -0
  118. data/lib/droonga/watch_schema.rb +92 -0
  119. data/lib/droonga/watcher.rb +257 -0
  120. data/lib/droonga/worker.rb +61 -0
  121. data/sample/cluster/catalog.json +42 -0
  122. data/sample/mecab_filter/data.grn +7 -0
  123. data/sample/mecab_filter/ddl.grn +7 -0
  124. data/sample/mecab_filter/search_with_mecab_filter.json +21 -0
  125. data/sample/mecab_filter/search_without_mecab_filter.json +21 -0
  126. data/test/command/config/default/catalog.json +85 -0
  127. data/test/command/config/default/fluentd.conf +11 -0
  128. data/test/command/config/version1/catalog.json +68 -0
  129. data/test/command/config/version1/fluentd.conf +11 -0
  130. data/test/command/fixture/documents.jsons +208 -0
  131. data/test/command/fixture/event.jsons +41 -0
  132. data/test/command/fixture/user-table-array.jsons +38 -0
  133. data/test/command/fixture/user-table.jsons +47 -0
  134. data/test/command/run-test.rb +34 -0
  135. data/test/command/suite/add/dimension/column.catalog.json +28 -0
  136. data/test/command/suite/add/dimension/column.expected +41 -0
  137. data/test/command/suite/add/dimension/column.test +51 -0
  138. data/test/command/suite/add/dimension/integer.catalog.json +19 -0
  139. data/test/command/suite/add/dimension/integer.expected +41 -0
  140. data/test/command/suite/add/dimension/integer.test +51 -0
  141. data/test/command/suite/add/error/invalid-integer.expected +46 -0
  142. data/test/command/suite/add/error/invalid-integer.test +12 -0
  143. data/test/command/suite/add/error/invalid-time.expected +46 -0
  144. data/test/command/suite/add/error/invalid-time.test +12 -0
  145. data/test/command/suite/add/error/missing-key.expected +25 -0
  146. data/test/command/suite/add/error/missing-key.test +16 -0
  147. data/test/command/suite/add/error/missing-table.expected +25 -0
  148. data/test/command/suite/add/error/missing-table.test +16 -0
  149. data/test/command/suite/add/error/unknown-column.expected +46 -0
  150. data/test/command/suite/add/error/unknown-column.test +12 -0
  151. data/test/command/suite/add/error/unknown-table.expected +25 -0
  152. data/test/command/suite/add/error/unknown-table.test +17 -0
  153. data/test/command/suite/add/minimum.expected +6 -0
  154. data/test/command/suite/add/minimum.test +11 -0
  155. data/test/command/suite/add/with-values.expected +6 -0
  156. data/test/command/suite/add/with-values.test +17 -0
  157. data/test/command/suite/add/without-key.expected +6 -0
  158. data/test/command/suite/add/without-key.test +16 -0
  159. data/test/command/suite/groonga/column_create/scalar.expected +26 -0
  160. data/test/command/suite/groonga/column_create/scalar.test +17 -0
  161. data/test/command/suite/groonga/column_create/unknown-table.expected +14 -0
  162. data/test/command/suite/groonga/column_create/unknown-table.test +7 -0
  163. data/test/command/suite/groonga/column_create/vector.expected +26 -0
  164. data/test/command/suite/groonga/column_create/vector.test +18 -0
  165. data/test/command/suite/groonga/column_list/success.expected +86 -0
  166. data/test/command/suite/groonga/column_list/success.test +24 -0
  167. data/test/command/suite/groonga/column_list/unknown-table.expected +13 -0
  168. data/test/command/suite/groonga/column_list/unknown-table.test +7 -0
  169. data/test/command/suite/groonga/column_remove/success.expected +39 -0
  170. data/test/command/suite/groonga/column_remove/success.test +25 -0
  171. data/test/command/suite/groonga/column_remove/unknown-column.expected +27 -0
  172. data/test/command/suite/groonga/column_remove/unknown-column.test +16 -0
  173. data/test/command/suite/groonga/column_remove/unknown-table.expected +14 -0
  174. data/test/command/suite/groonga/column_remove/unknown-table.test +7 -0
  175. data/test/command/suite/groonga/column_rename/success.expected +39 -0
  176. data/test/command/suite/groonga/column_rename/success.test +26 -0
  177. data/test/command/suite/groonga/column_rename/unknown-column.expected +27 -0
  178. data/test/command/suite/groonga/column_rename/unknown-column.test +16 -0
  179. data/test/command/suite/groonga/column_rename/unknown-table.expected +14 -0
  180. data/test/command/suite/groonga/column_rename/unknown-table.test +7 -0
  181. data/test/command/suite/groonga/delete/duplicated-identifiers.expected +27 -0
  182. data/test/command/suite/groonga/delete/duplicated-identifiers.test +17 -0
  183. data/test/command/suite/groonga/delete/filter.expected +19 -0
  184. data/test/command/suite/groonga/delete/filter.test +19 -0
  185. data/test/command/suite/groonga/delete/invalid-filter.expected +14 -0
  186. data/test/command/suite/groonga/delete/invalid-filter.test +9 -0
  187. data/test/command/suite/groonga/delete/no-identifier.expected +27 -0
  188. data/test/command/suite/groonga/delete/no-identifier.test +15 -0
  189. data/test/command/suite/groonga/delete/success.expected +19 -0
  190. data/test/command/suite/groonga/delete/success.test +19 -0
  191. data/test/command/suite/groonga/delete/unknown-table.expected +14 -0
  192. data/test/command/suite/groonga/delete/unknown-table.test +7 -0
  193. data/test/command/suite/groonga/select/minimum.expected +22 -0
  194. data/test/command/suite/groonga/select/minimum.test +8 -0
  195. data/test/command/suite/groonga/table_create/array.expected +14 -0
  196. data/test/command/suite/groonga/table_create/array.test +8 -0
  197. data/test/command/suite/groonga/table_create/hash.expected +13 -0
  198. data/test/command/suite/groonga/table_create/hash.test +8 -0
  199. data/test/command/suite/groonga/table_list/success.expected +71 -0
  200. data/test/command/suite/groonga/table_list/success.test +15 -0
  201. data/test/command/suite/groonga/table_remove/success.expected +13 -0
  202. data/test/command/suite/groonga/table_remove/success.test +8 -0
  203. data/test/command/suite/groonga/table_remove/unknown-table.expected +14 -0
  204. data/test/command/suite/groonga/table_remove/unknown-table.test +7 -0
  205. data/test/command/suite/message/error/missing-dataset.expected +9 -0
  206. data/test/command/suite/message/error/missing-dataset.test +5 -0
  207. data/test/command/suite/message/error/unknown-dataset.expected +9 -0
  208. data/test/command/suite/message/error/unknown-dataset.test +6 -0
  209. data/test/command/suite/message/error/unknown-type.expected +9 -0
  210. data/test/command/suite/message/error/unknown-type.test +6 -0
  211. data/test/command/suite/search/adjusters/multiple.catalog.json +38 -0
  212. data/test/command/suite/search/adjusters/multiple.expected +19 -0
  213. data/test/command/suite/search/adjusters/multiple.test +75 -0
  214. data/test/command/suite/search/adjusters/one.catalog.json +38 -0
  215. data/test/command/suite/search/adjusters/one.expected +19 -0
  216. data/test/command/suite/search/adjusters/one.test +66 -0
  217. data/test/command/suite/search/attributes/array.expected +21 -0
  218. data/test/command/suite/search/attributes/array.test +28 -0
  219. data/test/command/suite/search/attributes/hash.expected +30 -0
  220. data/test/command/suite/search/attributes/hash.test +36 -0
  221. data/test/command/suite/search/complex.expected +48 -0
  222. data/test/command/suite/search/complex.test +23 -0
  223. data/test/command/suite/search/condition/nested.expected +15 -0
  224. data/test/command/suite/search/condition/nested.test +27 -0
  225. data/test/command/suite/search/condition/query/nonexistent_column.catalog.json +37 -0
  226. data/test/command/suite/search/condition/query/nonexistent_column.expected +48 -0
  227. data/test/command/suite/search/condition/query/nonexistent_column.test +33 -0
  228. data/test/command/suite/search/condition/query/syntax_error.catalog.json +36 -0
  229. data/test/command/suite/search/condition/query/syntax_error.expected +48 -0
  230. data/test/command/suite/search/condition/query/syntax_error.test +33 -0
  231. data/test/command/suite/search/condition/query.expected +24 -0
  232. data/test/command/suite/search/condition/query.test +23 -0
  233. data/test/command/suite/search/condition/script.expected +24 -0
  234. data/test/command/suite/search/condition/script.test +26 -0
  235. data/test/command/suite/search/error/cyclic-source.expected +14 -0
  236. data/test/command/suite/search/error/cyclic-source.test +12 -0
  237. data/test/command/suite/search/error/deeply-cyclic-source.expected +17 -0
  238. data/test/command/suite/search/error/deeply-cyclic-source.test +15 -0
  239. data/test/command/suite/search/error/missing-source-parameter.expected +13 -0
  240. data/test/command/suite/search/error/missing-source-parameter.test +11 -0
  241. data/test/command/suite/search/error/no-query.expected +9 -0
  242. data/test/command/suite/search/error/no-query.test +7 -0
  243. data/test/command/suite/search/error/unknown-source.expected +52 -0
  244. data/test/command/suite/search/error/unknown-source.test +12 -0
  245. data/test/command/suite/search/group/count.expected +10 -0
  246. data/test/command/suite/search/group/count.test +18 -0
  247. data/test/command/suite/search/group/limit.expected +15 -0
  248. data/test/command/suite/search/group/limit.test +20 -0
  249. data/test/command/suite/search/group/string.expected +32 -0
  250. data/test/command/suite/search/group/string.test +40 -0
  251. data/test/command/suite/search/group/subrecord/with-sort.catalog.json +33 -0
  252. data/test/command/suite/search/group/subrecord/with-sort.expected +30 -0
  253. data/test/command/suite/search/group/subrecord/with-sort.test +81 -0
  254. data/test/command/suite/search/multiple/chained.expected +41 -0
  255. data/test/command/suite/search/multiple/chained.test +39 -0
  256. data/test/command/suite/search/multiple/parallel.expected +35 -0
  257. data/test/command/suite/search/multiple/parallel.test +35 -0
  258. data/test/command/suite/search/output/attributes/invalid.catalog.json +13 -0
  259. data/test/command/suite/search/output/attributes/invalid.expected +44 -0
  260. data/test/command/suite/search/output/attributes/invalid.test +28 -0
  261. data/test/command/suite/search/range/only-output.expected +24 -0
  262. data/test/command/suite/search/range/only-output.test +23 -0
  263. data/test/command/suite/search/range/only-sort.expected +24 -0
  264. data/test/command/suite/search/range/only-sort.test +26 -0
  265. data/test/command/suite/search/range/sort-and-output.expected +21 -0
  266. data/test/command/suite/search/range/sort-and-output.test +27 -0
  267. data/test/command/suite/search/range/too-large-output-offset.expected +12 -0
  268. data/test/command/suite/search/range/too-large-output-offset.test +23 -0
  269. data/test/command/suite/search/range/too-large-sort-offset.expected +12 -0
  270. data/test/command/suite/search/range/too-large-sort-offset.test +26 -0
  271. data/test/command/suite/search/response/elapsed_time.catalog.json +13 -0
  272. data/test/command/suite/search/response/elapsed_time.expected +11 -0
  273. data/test/command/suite/search/response/elapsed_time.test +26 -0
  274. data/test/command/suite/search/response/records/value/time.expected +20 -0
  275. data/test/command/suite/search/response/records/value/time.test +22 -0
  276. data/test/command/suite/search/simple.expected +48 -0
  277. data/test/command/suite/search/simple.test +22 -0
  278. data/test/command/suite/search/sort/default-offset-limit.expected +39 -0
  279. data/test/command/suite/search/sort/default-offset-limit.test +24 -0
  280. data/test/command/suite/search/sort/invisible-column.expected +24 -0
  281. data/test/command/suite/search/sort/invisible-column.test +26 -0
  282. data/test/command/suite/watch/subscribe.expected +6 -0
  283. data/test/command/suite/watch/subscribe.test +9 -0
  284. data/test/command/suite/watch/unsubscribe.expected +6 -0
  285. data/test/command/suite/watch/unsubscribe.test +9 -0
  286. data/test/performance/run-test.rb +56 -0
  287. data/test/performance/watch/catalog.json +33 -0
  288. data/test/performance/watch/feed.json +9 -0
  289. data/test/performance/watch/fluentd.conf +11 -0
  290. data/test/performance/watch/subscribe.json +3 -0
  291. data/test/unit/catalog/test_collection_volume.rb +103 -0
  292. data/test/unit/catalog/test_dataset.rb +104 -0
  293. data/test/unit/catalog/test_schema.rb +226 -0
  294. data/test/unit/catalog/test_single_volume.rb +31 -0
  295. data/test/unit/catalog/test_slice.rb +92 -0
  296. data/test/unit/catalog/test_version1.rb +361 -0
  297. data/test/unit/catalog/test_version2.rb +124 -0
  298. data/test/unit/catalog/test_version2_validator.rb +66 -0
  299. data/test/unit/catalog/test_volume_collection.rb +50 -0
  300. data/test/unit/fixtures/array.grn +18 -0
  301. data/test/unit/fixtures/catalog/version1.json +40 -0
  302. data/test/unit/fixtures/catalog/version2.json +62 -0
  303. data/test/unit/fixtures/document.grn +34 -0
  304. data/test/unit/fixtures/reference/array.grn +11 -0
  305. data/test/unit/fixtures/reference/hash.grn +7 -0
  306. data/test/unit/helper/distributed_search_planner_helper.rb +83 -0
  307. data/test/unit/helper/fixture.rb +28 -0
  308. data/test/unit/helper/plugin_helper.rb +38 -0
  309. data/test/unit/helper/sandbox.rb +86 -0
  310. data/test/unit/helper/stub_worker.rb +27 -0
  311. data/test/unit/helper/watch_helper.rb +23 -0
  312. data/test/unit/helper.rb +28 -0
  313. data/test/unit/plugins/crud/test_add.rb +190 -0
  314. data/test/unit/plugins/groonga/select/test_adapter_input.rb +510 -0
  315. data/test/unit/plugins/groonga/select/test_adapter_output.rb +201 -0
  316. data/test/unit/plugins/groonga/test_column_create.rb +171 -0
  317. data/test/unit/plugins/groonga/test_column_list.rb +170 -0
  318. data/test/unit/plugins/groonga/test_column_remove.rb +98 -0
  319. data/test/unit/plugins/groonga/test_column_rename.rb +105 -0
  320. data/test/unit/plugins/groonga/test_delete.rb +127 -0
  321. data/test/unit/plugins/groonga/test_table_create.rb +147 -0
  322. data/test/unit/plugins/groonga/test_table_list.rb +184 -0
  323. data/test/unit/plugins/groonga/test_table_remove.rb +61 -0
  324. data/test/unit/plugins/search/planner/test_basic.rb +120 -0
  325. data/test/unit/plugins/search/planner/test_group_by.rb +573 -0
  326. data/test/unit/plugins/search/planner/test_output.rb +388 -0
  327. data/test/unit/plugins/search/planner/test_sort_by.rb +938 -0
  328. data/test/unit/plugins/search/test_collector.rb +806 -0
  329. data/test/unit/plugins/search/test_handler.rb +930 -0
  330. data/test/unit/plugins/search/test_planner.rb +174 -0
  331. data/test/unit/plugins/test_basic.rb +510 -0
  332. data/test/unit/plugins/test_groonga.rb +70 -0
  333. data/test/unit/plugins/test_watch.rb +211 -0
  334. data/test/unit/run-test.rb +56 -0
  335. data/test/unit/test_catalog_generator.rb +93 -0
  336. data/test/unit/test_message_matcher.rb +160 -0
  337. data/test/unit/test_schema_applier.rb +59 -0
  338. data/test/unit/test_sweeper.rb +95 -0
  339. data/test/unit/test_watch_schema.rb +57 -0
  340. data/test/unit/test_watcher.rb +336 -0
  341. metadata +759 -0
@@ -0,0 +1,87 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2013 Droonga Project
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License version 2.1 as published by the Free Software Foundation.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+
18
+ require "tsort"
19
+
20
+ module Droonga
21
+ class Distributor
22
+ class UndefinedInputError < StandardError
23
+ attr_reader :input
24
+ def initialize(input)
25
+ @input = input
26
+ super("undefined input assigned: <#{input}>")
27
+ end
28
+ end
29
+
30
+ class CyclicStepsError < StandardError
31
+ attr_reader :steps
32
+ def initialize(steps)
33
+ @steps = steps
34
+ super("cyclic steps found: <#{steps}>")
35
+ end
36
+ end
37
+
38
+ include TSort
39
+
40
+ def initialize(dispatcher, plan)
41
+ @dispatcher = dispatcher
42
+ @plan = plan
43
+ build_dependencies
44
+ end
45
+
46
+ def distribute
47
+ steps = []
48
+ each_strongly_connected_component do |nodes|
49
+ raise CyclicStepsError.new(nodes) if nodes.size > 1
50
+ nodes.each do |node|
51
+ steps << @step_maps[node] if node.is_a?(Integer)
52
+ end
53
+ end
54
+ @dispatcher.dispatch_steps(steps)
55
+ end
56
+
57
+ private
58
+ def build_dependencies
59
+ @dependencies = {}
60
+ @step_maps = {}
61
+ step_id = 0
62
+ @plan.each do |step|
63
+ step_id += 1
64
+ # Integer#hash (step_id.hash) is very faster than Hash#hash (step.hash).
65
+ @step_maps[step_id] = step
66
+ @dependencies[step_id] = step["inputs"]
67
+ next unless step["outputs"]
68
+ step["outputs"].each do |output|
69
+ @dependencies[output] = [step_id]
70
+ end
71
+ end
72
+ end
73
+
74
+ def tsort_each_node(&block)
75
+ @dependencies.each_key(&block)
76
+ end
77
+
78
+ def tsort_each_child(node, &block)
79
+ if node.is_a? String and @dependencies[node].nil?
80
+ raise UndefinedInputError.new(node)
81
+ end
82
+ if @dependencies[node]
83
+ @dependencies[node].each(&block)
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,441 @@
1
+ # Copyright (C) 2014 Droonga Project
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License version 2.1 as published by the Free Software Foundation.
6
+ #
7
+ # This library is distributed in the hope that it will be useful,
8
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10
+ # Lesser General Public License for more details.
11
+ #
12
+ # You should have received a copy of the GNU Lesser General Public
13
+ # License along with this library; if not, write to the Free Software
14
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
+
16
+ require "optparse"
17
+ require "socket"
18
+ require "ipaddr"
19
+ require "fileutils"
20
+
21
+ require "droonga/engine"
22
+ require "droonga/event_loop"
23
+ require "droonga/fluent_message_receiver"
24
+ require "droonga/plugin_loader"
25
+
26
+ module Droonga
27
+ class Engine
28
+ module Command
29
+ module DroongaEngine
30
+ module Signals
31
+ include ServerEngine::Daemon::Signals
32
+ end
33
+
34
+ class Configuration
35
+ DEFAULT_HOST = Socket.gethostname
36
+ DEFAULT_PORT = 10031
37
+
38
+ attr_reader :host, :port, :tag, :log_file, :pid_file
39
+ def initialize
40
+ @host = DEFAULT_HOST
41
+ @port = DEFAULT_PORT
42
+ @tag = "droonga"
43
+ @log_file = nil
44
+ @daemon = false
45
+ @pid_file = nil
46
+ end
47
+
48
+ def engine_name
49
+ "#{@host}:#{@port}/#{@tag}"
50
+ end
51
+
52
+ def address_family
53
+ ip_address = IPAddr.new(IPSocket.getaddress(@host))
54
+ ip_address.family
55
+ end
56
+
57
+ def log_level
58
+ ENV["DROONGA_LOG_LEVEL"] || Logger::Level.default_label
59
+ end
60
+
61
+ def daemon?
62
+ @daemon
63
+ end
64
+
65
+ def to_command_line
66
+ [
67
+ "--host", @host,
68
+ "--port", @port.to_s,
69
+ "--tag", @tag,
70
+ "--log-level", log_level,
71
+ ]
72
+ end
73
+
74
+ def add_command_line_options(parser)
75
+ add_connection_options(parser)
76
+ add_log_options(parser)
77
+ add_process_options(parser)
78
+ end
79
+
80
+ private
81
+ def add_connection_options(parser)
82
+ parser.separator("")
83
+ parser.separator("Connection:")
84
+ parser.on("--host=HOST",
85
+ "The host name of the Droonga engine",
86
+ "(#{@host})") do |host|
87
+ @host = host
88
+ end
89
+ parser.on("--port=PORT", Integer,
90
+ "The port number of the Droonga engine",
91
+ "(#{@port})") do |port|
92
+ @port = port
93
+ end
94
+ parser.on("--tag=TAG",
95
+ "The tag of the Droonga engine",
96
+ "(#{@tag})") do |tag|
97
+ @tag = tag
98
+ end
99
+ end
100
+
101
+ def add_log_options(parser)
102
+ parser.separator("")
103
+ parser.separator("Log:")
104
+ levels = Logger::Level::LABELS
105
+ levels_label = levels.join(",")
106
+ parser.on("--log-level=LEVEL", levels,
107
+ "The log level of the Droonga engine",
108
+ "[#{levels_label}]",
109
+ "(#{log_level})") do |level|
110
+ ENV["DROONGA_LOG_LEVEL"] = level
111
+ end
112
+ parser.on("--log-file=FILE",
113
+ "Output logs to FILE") do |file|
114
+ @log_file = file
115
+ end
116
+ end
117
+
118
+ def add_process_options(parser)
119
+ parser.separator("")
120
+ parser.separator("Process:")
121
+ parser.on("--daemon",
122
+ "Run as a daemon") do
123
+ @daemon = true
124
+ end
125
+ parser.on("--pid-file=FILE",
126
+ "Put PID to the FILE") do |file|
127
+ @pid_file = file
128
+ end
129
+ end
130
+ end
131
+
132
+ class Supervisor
133
+ class << self
134
+ def run(command_line_arguments)
135
+ new.run(command_line_arguments)
136
+ end
137
+ end
138
+
139
+ def initialize
140
+ @configuration = Configuration.new
141
+ @log_output = nil
142
+ end
143
+
144
+ def run(command_line_arguments)
145
+ parse_command_line_arguments!(command_line_arguments)
146
+
147
+ @listen_socket = TCPServer.new(@configuration.host,
148
+ @configuration.port)
149
+ @heartbeat_socket = UDPSocket.new(@configuration.address_family)
150
+ @heartbeat_socket.bind(@configuration.host,
151
+ @configuration.port)
152
+
153
+ if @configuration.daemon?
154
+ ENV["DROONGA_CATALOG"] ||= "catalog.json"
155
+ ENV["DROONGA_CATALOG"] = File.expand_path(ENV["DROONGA_CATALOG"])
156
+ Process.daemon
157
+ end
158
+
159
+ open_log_file do
160
+ write_pid_file do
161
+ run_main_loop
162
+ end
163
+ end
164
+ end
165
+
166
+ private
167
+ def parse_command_line_arguments!(command_line_arguments)
168
+ parser = OptionParser.new
169
+ @configuration.add_command_line_options(parser)
170
+ parser.parse!(command_line_arguments)
171
+ end
172
+
173
+ def run_service(ready_notify_fd=nil)
174
+ listen_fd = @listen_socket.fileno
175
+ heartbeat_fd = @heartbeat_socket.fileno
176
+ env = {}
177
+ command_line = [
178
+ RbConfig.ruby,
179
+ "-S",
180
+ "#{$0}-service",
181
+ "--listen-fd", listen_fd.to_s,
182
+ "--heartbeat-fd", heartbeat_fd.to_s,
183
+ *@configuration.to_command_line
184
+ ]
185
+ options = {
186
+ listen_fd => listen_fd,
187
+ heartbeat_fd => heartbeat_fd,
188
+ }
189
+ if ready_notify_fd
190
+ command_line.push("--ready-notify-fd", ready_notify_fd.to_s)
191
+ options[ready_notify_fd] = ready_notify_fd
192
+ end
193
+ if @log_output
194
+ options[:out] = @log_output
195
+ options[:err] = @log_output
196
+ end
197
+ spawn(env, *command_line, options)
198
+ end
199
+
200
+ def run_main_loop
201
+ service_pid = nil
202
+ running = true
203
+
204
+ trap(:INT) do
205
+ Process.kill(:INT, service_pid)
206
+ running = false
207
+ end
208
+ trap(Signals::GRACEFUL_STOP) do
209
+ Process.kill(Signals::GRACEFUL_STOP, service_pid)
210
+ running = false
211
+ end
212
+ trap(Signals::IMMEDIATE_STOP) do
213
+ Process.kill(Signals::IMMEDIATE_STOP, service_pid)
214
+ running = false
215
+ end
216
+ trap(Signals::GRACEFUL_RESTART) do
217
+ old_service_pid = service_pid
218
+ IO.pipe do |ready_notify_read_io, ready_notify_write_io|
219
+ service_pid = run_service(ready_notify_write_io.fileno)
220
+ ready_notify_write_io.close
221
+ IO.select([ready_notify_read_io])
222
+ Process.kill(Signals::GRACEFUL_STOP, old_service_pid)
223
+ end
224
+ end
225
+ trap(Signals::IMMEDIATE_RESTART) do
226
+ old_service_pid = service_pid
227
+ service_pid = run_service
228
+ Process.kill(Signals::IMMEDIATE_STOP, old_service_pid)
229
+ end
230
+
231
+ succeeded = true
232
+ while running
233
+ service_pid ||= run_service
234
+ finished_pid, status = Process.waitpid2(service_pid)
235
+ service_pid = nil if service_pid == finished_pid
236
+ if status.nil?
237
+ succeeded = false
238
+ break
239
+ end
240
+ unless status.success?
241
+ succeeded = false
242
+ break
243
+ end
244
+ end
245
+
246
+ succeeded
247
+ end
248
+
249
+ def open_log_file
250
+ if @configuration.log_file
251
+ File.open(@configuration.log_file, "a") do |file|
252
+ @log_output = file
253
+ yield
254
+ end
255
+ else
256
+ yield
257
+ end
258
+ end
259
+
260
+ def write_pid_file
261
+ if @configuration.pid_file
262
+ File.open(@configuration.pid_file, "w") do |file|
263
+ file.puts(Process.pid)
264
+ end
265
+ begin
266
+ yield
267
+ ensure
268
+ FileUtils.rm_f(@configuration.pid_file)
269
+ end
270
+ else
271
+ yield
272
+ end
273
+ end
274
+ end
275
+
276
+ class Service
277
+ class << self
278
+ def run(command_line_arguments)
279
+ new.run(command_line_arguments)
280
+ end
281
+ end
282
+
283
+ def initialize
284
+ @configuration = Configuration.new
285
+ @listen_fd = nil
286
+ @heartbeat_fd = nil
287
+ @ready_notiofy_fd = nil
288
+ end
289
+
290
+ def run(command_line_arguments)
291
+ parse_command_line_arguments!(command_line_arguments)
292
+ PluginLoader.load_all
293
+
294
+ begin
295
+ run_services
296
+ ensure
297
+ shutdown_services
298
+ end
299
+
300
+ true
301
+ end
302
+
303
+ private
304
+ def parse_command_line_arguments!(command_line_arguments)
305
+ parser = OptionParser.new
306
+ @configuration.add_command_line_options(parser)
307
+ add_internal_options(parser)
308
+ parser.parse!(command_line_arguments)
309
+ end
310
+
311
+ def add_internal_options(parser)
312
+ parser.separator("")
313
+ parser.separator("Internal:")
314
+ parser.on("--listen-fd=FD", Integer,
315
+ "Use FD as the listen file descriptor") do |fd|
316
+ @listen_fd = fd
317
+ end
318
+ parser.on("--heartbeat-fd=FD", Integer,
319
+ "Use FD as the heartbeat file descriptor") do |fd|
320
+ @heartbeat_fd = fd
321
+ end
322
+ parser.on("--ready-notify-fd=FD", Integer,
323
+ "Use FD for notifying the service ready") do |fd|
324
+ @ready_notify_fd = fd
325
+ end
326
+ end
327
+
328
+ def run_services
329
+ @engine = nil
330
+ @receiver = nil
331
+ raw_loop = Coolio::Loop.default
332
+ @loop = EventLoop.new(raw_loop)
333
+
334
+ run_engine
335
+ run_receiver
336
+ setup_signals
337
+ notify_ready
338
+ @loop.run
339
+ end
340
+
341
+ def shutdown_services
342
+ shutdown_receiver
343
+ shutdown_engine
344
+ @loop = nil
345
+ end
346
+
347
+ def run_engine
348
+ @engine = Engine.new(@loop, @configuration.engine_name)
349
+ @engine.start
350
+ end
351
+
352
+ def shutdown_engine
353
+ return if @engine.nil?
354
+ @engine.shutdown
355
+ @engine = nil
356
+ end
357
+
358
+ def run_receiver
359
+ @receiver = create_receiver
360
+ @receiver.start
361
+ end
362
+
363
+ def shutdown_receiver
364
+ return if @receiver.nil?
365
+ @receiver.shutdown
366
+ @receiver = nil
367
+ end
368
+
369
+ def create_receiver
370
+ options = {
371
+ :host => @host,
372
+ :port => @port,
373
+ :listen_fd => @listen_fd,
374
+ :heartbeat_fd => @heartbeat_fd,
375
+ }
376
+ FluentMessageReceiver.new(@loop, options) do |tag, time, record|
377
+ on_message(tag, time, record)
378
+ end
379
+ end
380
+
381
+ def on_message(tag, time, record)
382
+ prefix, type, *arguments = tag.split(/\./)
383
+ if type.nil? or type.empty? or type == "message"
384
+ message = record
385
+ else
386
+ message = {
387
+ "type" => type,
388
+ "arguments" => arguments,
389
+ "body" => record
390
+ }
391
+ end
392
+ reply_to = message["replyTo"]
393
+ if reply_to.is_a? String
394
+ message["replyTo"] = {
395
+ "type" => "#{message["type"]}.result",
396
+ "to" => reply_to
397
+ }
398
+ end
399
+
400
+ @engine.process(message)
401
+ end
402
+
403
+ def setup_signals
404
+ trap(Signals::GRACEFUL_STOP) do
405
+ stop_graceful
406
+ end
407
+ trap(Signals::IMMEDIATE_STOP) do
408
+ stop_immediate
409
+ end
410
+ trap(:INT) do
411
+ stop_immediate
412
+ trap(:INT, "DEFAULT")
413
+ end
414
+ end
415
+
416
+ def stop_graceful
417
+ @loop.stop if @loop
418
+ end
419
+
420
+ def stop_immediate
421
+ stop_graceful
422
+ shutdown_services
423
+ end
424
+
425
+ def notify_ready
426
+ return if @ready_notify_fd.nil?
427
+ ready_notify_io = IO.new(@ready_notify_fd)
428
+ @ready_notify_fd = nil
429
+ watcher = Coolio::IOWatcher.new(ready_notify_io, "w")
430
+ @loop.attach(watcher)
431
+ watcher.on_writable do
432
+ ready_notify_io.write("ready\n")
433
+ ready_notify_io.close
434
+ detach
435
+ end
436
+ end
437
+ end
438
+ end
439
+ end
440
+ end
441
+ end
@@ -0,0 +1,20 @@
1
+ # Copyright (C) 2014 Droonga Project
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License version 2.1 as published by the Free Software Foundation.
6
+ #
7
+ # This library is distributed in the hope that it will be useful,
8
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10
+ # Lesser General Public License for more details.
11
+ #
12
+ # You should have received a copy of the GNU Lesser General Public
13
+ # License along with this library; if not, write to the Free Software
14
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
+
16
+ module Droonga
17
+ class Engine
18
+ VERSION = "1.0.1"
19
+ end
20
+ end
@@ -0,0 +1,80 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2013-2014 Droonga Project
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License version 2.1 as published by the Free Software Foundation.
8
+ #
9
+ # This library is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ # Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public
15
+ # License along with this library; if not, write to the Free Software
16
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+
18
+ require "droonga/engine/version"
19
+ require "droonga/loggable"
20
+ require "droonga/engine_state"
21
+ require "droonga/catalog_observer"
22
+ require "droonga/dispatcher"
23
+
24
+ module Droonga
25
+ class Engine
26
+ include Loggable
27
+
28
+ def initialize(loop, name)
29
+ @state = EngineState.new(loop, name)
30
+ @catalog_observer = Droonga::CatalogObserver.new(@state.loop)
31
+ @catalog_observer.on_reload = lambda do |catalog|
32
+ graceful_restart(catalog)
33
+ logger.info("restarted")
34
+ end
35
+ end
36
+
37
+ def start
38
+ logger.trace("start: start")
39
+ @state.start
40
+ @catalog_observer.start
41
+ catalog = @catalog_observer.catalog
42
+ @dispatcher = create_dispatcher(catalog)
43
+ @dispatcher.start
44
+ logger.trace("start: done")
45
+ end
46
+
47
+ def shutdown
48
+ logger.trace("shutdown: start")
49
+ @catalog_observer.stop
50
+ @dispatcher.shutdown
51
+ @state.shutdown
52
+ logger.trace("shutdown: done")
53
+ end
54
+
55
+ def process(message)
56
+ @dispatcher.process_message(message)
57
+ end
58
+
59
+ private
60
+ def create_dispatcher(catalog)
61
+ Dispatcher.new(@state, catalog)
62
+ end
63
+
64
+ def graceful_restart(catalog)
65
+ logger.trace("graceful_restart: start")
66
+ old_dispatcher = @dispatcher
67
+ logger.trace("graceful_restart: creating new dispatcher")
68
+ new_dispatcher = create_dispatcher(catalog)
69
+ new_dispatcher.start
70
+ @dispatcher = new_dispatcher
71
+ logger.trace("graceful_restart: shutdown old dispatcher")
72
+ old_dispatcher.shutdown
73
+ logger.trace("graceful_restart: done")
74
+ end
75
+
76
+ def log_tag
77
+ "engine"
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,79 @@
1
+ # Copyright (C) 2014 Droonga Project
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License version 2.1 as published by the Free Software Foundation.
6
+ #
7
+ # This library is distributed in the hope that it will be useful,
8
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10
+ # Lesser General Public License for more details.
11
+ #
12
+ # You should have received a copy of the GNU Lesser General Public
13
+ # License along with this library; if not, write to the Free Software
14
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
+
16
+ require "coolio"
17
+
18
+ require "droonga/loggable"
19
+ require "droonga/event_loop"
20
+ require "droonga/forwarder"
21
+ require "droonga/replier"
22
+
23
+ module Droonga
24
+ class EngineState
25
+ include Loggable
26
+
27
+ attr_reader :loop
28
+ attr_reader :name
29
+ attr_reader :forwarder
30
+ attr_reader :replier
31
+ def initialize(loop, name)
32
+ @loop = loop
33
+ @name = name
34
+ @sessions = {}
35
+ @current_id = 0
36
+ @forwarder = Forwarder.new(@loop)
37
+ @replier = Replier.new(@forwarder)
38
+ end
39
+
40
+ def start
41
+ logger.trace("start start")
42
+ @forwarder.start
43
+ logger.trace("start done")
44
+ end
45
+
46
+ def shutdown
47
+ logger.trace("shutdown: start")
48
+ @forwarder.shutdown
49
+ logger.trace("shutdown: done")
50
+ end
51
+
52
+ def local_route?(route)
53
+ route.start_with?(@name)
54
+ end
55
+
56
+ def generate_id
57
+ id = @current_id
58
+ @current_id = id.succ
59
+ return [@name, id].join(".#")
60
+ end
61
+
62
+ def find_session(id)
63
+ @sessions[id]
64
+ end
65
+
66
+ def register_session(id, session)
67
+ @sessions[id] = session
68
+ end
69
+
70
+ def unregister_session(id)
71
+ @sessions.delete(id)
72
+ end
73
+
74
+ private
75
+ def log_tag
76
+ "engine_state"
77
+ end
78
+ end
79
+ end