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,64 @@
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 "droonga/loggable"
17
+ require "droonga/message_matcher"
18
+ require "droonga/collector"
19
+ require "droonga/collector_message"
20
+
21
+ module Droonga
22
+ class CollectorRunner
23
+ include Loggable
24
+
25
+ def initialize(plugins)
26
+ default_plugins = ["basic"]
27
+ plugins += (default_plugins - plugins)
28
+ @collector_classes = Collector.find_sub_classes(plugins)
29
+ end
30
+
31
+ def shutdown
32
+ end
33
+
34
+ def collect(message)
35
+ collector_message = CollectorMessage.new(message)
36
+ logger.trace("collect: start",
37
+ :type => collector_message.type)
38
+ collector_class = find_collector_class(message)
39
+ if collector_class.nil?
40
+ raise UnsupportedMessageError.new(:collector, message)
41
+ end
42
+ collector = collector_class.new
43
+ collector.collect(collector_message)
44
+ logger.trace("collector: done")
45
+ end
46
+
47
+ private
48
+ def find_collector_class(message)
49
+ @collector_classes.find do |collector_class|
50
+ pattern = collector_class.message.pattern
51
+ if pattern
52
+ matcher = MessageMatcher.new(pattern)
53
+ matcher.match?(message)
54
+ else
55
+ false
56
+ end
57
+ end
58
+ end
59
+
60
+ def log_tag
61
+ "collector-runner"
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,26 @@
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
+ module Collectors
18
+ class And
19
+ class << self
20
+ def operator
21
+ "and"
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
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
+ module Collectors
18
+ class Or
19
+ class << self
20
+ def operator
21
+ "or"
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
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
+ module Collectors
18
+ class Sum
19
+ class << self
20
+ def operator
21
+ "sum"
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,18 @@
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 "droonga/collectors/and"
17
+ require "droonga/collectors/or"
18
+ require "droonga/collectors/sum"
@@ -0,0 +1,326 @@
1
+ # Copyright (C) 2013-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 "English"
17
+ require "tsort"
18
+
19
+ require "droonga/loggable"
20
+ require "droonga/adapter_runner"
21
+ require "droonga/collector_runner"
22
+ require "droonga/step_runner"
23
+ require "droonga/farm"
24
+ require "droonga/session"
25
+ require "droonga/error_messages"
26
+ require "droonga/distributor"
27
+
28
+ module Droonga
29
+ class Dispatcher
30
+ include Loggable
31
+
32
+ class MissingDatasetParameter < ErrorMessages::BadRequest
33
+ def initialize
34
+ super("Missing required parameter: <dataset>")
35
+ end
36
+ end
37
+
38
+ class UnknownDataset < ErrorMessages::NotFound
39
+ def initialize(dataset)
40
+ super("Unknown dataset: <#{dataset}>")
41
+ end
42
+ end
43
+
44
+ class UnknownType < ErrorMessages::BadRequest
45
+ def initialize(type, dataset)
46
+ super("[#{dataset}] Handler not found for the type: <#{type}>")
47
+ end
48
+ end
49
+
50
+ def initialize(engine_state, catalog)
51
+ @engine_state = engine_state
52
+ @catalog = catalog
53
+ @adapter_runners = create_adapter_runners
54
+ @farm = Farm.new(@engine_state.name, @catalog, @engine_state.loop,
55
+ :dispatcher => self)
56
+ @collector_runners = create_collector_runners
57
+ @step_runners = create_step_runners
58
+ @forwarder = @engine_state.forwarder
59
+ @replier = @engine_state.replier
60
+ end
61
+
62
+ def start
63
+ @farm.start
64
+ end
65
+
66
+ def shutdown
67
+ @collector_runners.each_value do |collector_runner|
68
+ collector_runner.shutdown
69
+ end
70
+ @adapter_runners.each_value do |adapter_runner|
71
+ adapter_runner.shutdown
72
+ end
73
+ @farm.shutdown
74
+ end
75
+
76
+ def process_message(message)
77
+ @message = message
78
+ if message["type"] == "dispatcher"
79
+ process_internal_message(message["body"])
80
+ else
81
+ begin
82
+ assert_valid_message(message)
83
+ process_input_message(message)
84
+ rescue ErrorMessage => error
85
+ reply("statusCode" => error.status_code,
86
+ "body" => error.response_body)
87
+ rescue StandardError, LoadError, SyntaxError => error
88
+ logger.exception("failed to process input message", error)
89
+ formatted_error = ErrorMessages::InternalServerError.new("Unknown internal error")
90
+ reply("statusCode" => formatted_error.status_code,
91
+ "body" => formatted_error.response_body)
92
+ end
93
+ end
94
+ end
95
+
96
+ def forward(message, destination)
97
+ logger.trace("forward start")
98
+ @forwarder.forward(message, destination)
99
+ logger.trace("forward done")
100
+ end
101
+
102
+ # Replies response to replyTo.
103
+ #
104
+ # @param [Hash] message
105
+ # The message to be replied. See {Replier#reply} for available keys.
106
+ #
107
+ # The key-value pairs in request message are used as the default
108
+ # key-value pairs. For example, if the passed message doesn't
109
+ # include `id` key, `id` key's value is used in request message.
110
+ #
111
+ # @return [void]
112
+ #
113
+ # @see Replier#reply
114
+ def reply(message)
115
+ adapted_message = @message.merge(message)
116
+ adapter_runner = @adapter_runners[adapted_message["dataset"]]
117
+ if adapter_runner
118
+ adapted_message = adapter_runner.adapt_output(adapted_message)
119
+ end
120
+ if adapted_message["replyTo"].nil?
121
+ status_code = adapted_message["statusCode"] || 200
122
+ if status_code != 200
123
+ dataset = adapted_message["dataset"]
124
+ body = adapted_message["body"] || {}
125
+ name = body["name"] || "Unknown"
126
+ message = body["message"] || "unknown error"
127
+ logger.error("orphan error: " +
128
+ "<#{dataset}>[#{name}](#{status_code}): #{message}")
129
+ end
130
+ else
131
+ @replier.reply(adapted_message)
132
+ end
133
+ end
134
+
135
+ def process_internal_message(message)
136
+ id = message["id"]
137
+ session = @engine_state.find_session(id)
138
+ if session
139
+ session.receive(message["input"], message["value"])
140
+ else
141
+ steps = message["steps"]
142
+ if steps
143
+ session_planner = SessionPlanner.new(self, steps)
144
+ dataset = message["dataset"] || @message["dataset"]
145
+ collector_runner = @collector_runners[dataset]
146
+ session = session_planner.create_session(id, collector_runner)
147
+ @engine_state.register_session(id, session)
148
+ else
149
+ #todo: take cases receiving result before its query into account
150
+ end
151
+ session.start
152
+ end
153
+ @engine_state.unregister_session(id) if session.done?
154
+ end
155
+
156
+ def dispatch(message, destination)
157
+ if local?(destination)
158
+ process_internal_message(message)
159
+ else
160
+ @forwarder.forward(@message.merge("body" => message),
161
+ "type" => "dispatcher",
162
+ "to" => destination)
163
+ end
164
+ end
165
+
166
+ def dispatch_steps(steps)
167
+ id = @engine_state.generate_id
168
+ destinations = {}
169
+ steps.each do |step|
170
+ dataset = step["dataset"]
171
+ if dataset
172
+ routes = @catalog.get_routes(dataset, step)
173
+ step["routes"] = routes
174
+ else
175
+ step["routes"] ||= [id]
176
+ end
177
+ routes = step["routes"]
178
+ routes.each do |route|
179
+ destinations[farm_path(route)] = true
180
+ end
181
+ end
182
+ dispatch_message = { "id" => id, "steps" => steps }
183
+ destinations.each_key do |destination|
184
+ dispatch(dispatch_message, destination)
185
+ end
186
+ end
187
+
188
+ def process_local_message(local_message)
189
+ task = local_message["task"]
190
+ slice_name = task["route"]
191
+ step = task["step"]
192
+ command = step["command"]
193
+ descendants = {}
194
+ step["descendants"].each do |name, routes|
195
+ descendants[name] = routes.collect do |route|
196
+ farm_path(route)
197
+ end
198
+ end
199
+ local_message["descendants"] = descendants
200
+ farm_message = @message.merge("body" => local_message,
201
+ "type" => command)
202
+ @farm.process(slice_name, farm_message)
203
+ end
204
+
205
+ def local?(route)
206
+ @engine_state.local_route?(route)
207
+ end
208
+
209
+ private
210
+ def farm_path(route)
211
+ if route =~ /\A.*:\d+\/[^\.]+/
212
+ $MATCH
213
+ else
214
+ route
215
+ end
216
+ end
217
+
218
+ def process_input_message(message)
219
+ dataset = message["dataset"]
220
+ adapter_runner = @adapter_runners[dataset]
221
+ adapted_message = adapter_runner.adapt_input(message)
222
+ step_runner = @step_runners[dataset]
223
+ plan = step_runner.plan(adapted_message)
224
+ distributor = Distributor.new(self, plan)
225
+ distributor.distribute
226
+ rescue Droonga::UnsupportedMessageError => error
227
+ target_message = error.message
228
+ raise UnknownType.new(target_message["type"], target_message["dataset"])
229
+ end
230
+
231
+ def assert_valid_message(message)
232
+ unless message.key?("dataset")
233
+ raise MissingDatasetParameter.new
234
+ end
235
+ dataset = message["dataset"]
236
+ unless @catalog.have_dataset?(dataset)
237
+ raise UnknownDataset.new(dataset)
238
+ end
239
+ end
240
+
241
+ def create_runners
242
+ runners = {}
243
+ @catalog.datasets.each do |name, dataset|
244
+ runners[name] = yield(dataset)
245
+ end
246
+ runners
247
+ end
248
+
249
+ def create_adapter_runners
250
+ create_runners do |dataset|
251
+ AdapterRunner.new(self, dataset.plugins)
252
+ end
253
+ end
254
+
255
+ def create_collector_runners
256
+ create_runners do |dataset|
257
+ CollectorRunner.new(dataset.plugins)
258
+ end
259
+ end
260
+
261
+ def create_step_runners
262
+ create_runners do |dataset|
263
+ StepRunner.new(dataset, dataset.plugins)
264
+ end
265
+ end
266
+
267
+ def log_tag
268
+ "[#{Process.ppid}][#{Process.pid}] dispatcher"
269
+ end
270
+
271
+ class SessionPlanner
272
+ attr_reader :steps
273
+
274
+ def initialize(dispatcher, steps)
275
+ @dispatcher = dispatcher
276
+ @steps = steps
277
+ end
278
+
279
+ def create_session(id, collector_runner)
280
+ resolve_descendants
281
+ tasks = []
282
+ inputs = {}
283
+ @steps.each do |step|
284
+ step["routes"].each do |route|
285
+ next unless @dispatcher.local?(route)
286
+ task = {
287
+ "route" => route,
288
+ "step" => step,
289
+ "n_of_inputs" => 0,
290
+ "values" => {}
291
+ }
292
+ tasks << task
293
+ (step["inputs"] || [nil]).each do |input|
294
+ inputs[input] ||= []
295
+ inputs[input] << task
296
+ end
297
+ end
298
+ end
299
+ Session.new(id, @dispatcher, collector_runner, tasks, inputs)
300
+ end
301
+
302
+ def resolve_descendants
303
+ @descendants = {}
304
+ @steps.size.times do |index|
305
+ step = @steps[index]
306
+ (step["inputs"] || []).each do |input|
307
+ @descendants[input] ||= []
308
+ @descendants[input] << index
309
+ end
310
+ step["n_of_expects"] = 0
311
+ end
312
+ @steps.each do |step|
313
+ descendants = {}
314
+ (step["outputs"] || []).each do |output|
315
+ descendants[output] = []
316
+ @descendants[output].each do |index|
317
+ @steps[index]["n_of_expects"] += step["routes"].size
318
+ descendants[output].concat(@steps[index]["routes"])
319
+ end
320
+ end
321
+ step["descendants"] = descendants
322
+ end
323
+ end
324
+ end
325
+ end
326
+ end
@@ -0,0 +1,179 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 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
+ module Droonga
19
+ class DistributedCommandPlanner
20
+ attr_accessor :key, :dataset
21
+
22
+ REDUCE_SUM = "sum"
23
+
24
+ DEFAULT_LIMIT = -1
25
+
26
+ def initialize(source_message)
27
+ @source_message = source_message
28
+
29
+ @key = nil
30
+ @dataset = nil
31
+ @outputs = []
32
+
33
+ @reducers = []
34
+ @gatherers = []
35
+ @processor = nil
36
+
37
+ plan_errors_handling
38
+ end
39
+
40
+ def plan
41
+ unified_reducers + unified_gatherers + [fixed_processor]
42
+ end
43
+
44
+ def reduce(params=nil)
45
+ return unless params
46
+ params.each do |name, reducer|
47
+ gatherer = nil
48
+ if reducer.is_a?(Hash) and reducer[:gather]
49
+ gatherer = reducer[:gather]
50
+ reducer = reducer[:reduce]
51
+ end
52
+ @reducers << reducer_message(reduce_command, name, reducer)
53
+ @gatherers << gatherer_message(gather_command, name, gatherer)
54
+ @outputs << name
55
+ end
56
+ end
57
+
58
+ def scatter(record, options={})
59
+ @processor = {
60
+ "command" => @source_message["type"],
61
+ "dataset" => @dataset || @source_message["dataset"],
62
+ "body" => options[:body] || @source_message["body"],
63
+ "record" => record,
64
+ "type" => "scatter",
65
+ "outputs" => [],
66
+ "replica" => "all",
67
+ "post" => true
68
+ }
69
+ end
70
+
71
+ def broadcast(options={})
72
+ processor = {
73
+ "command" => @source_message["type"],
74
+ "dataset" => @dataset || @source_message["dataset"],
75
+ "body" => options[:body] || @source_message["body"],
76
+ "type" => "broadcast",
77
+ "outputs" => [],
78
+ "replica" => "random"
79
+ }
80
+ if options[:write]
81
+ processor["replica"] = "all"
82
+ processor["post"] = true
83
+ end
84
+ @processor = processor
85
+ end
86
+
87
+ private
88
+ def reduce_command
89
+ "reduce"
90
+ end
91
+
92
+ def gather_command
93
+ "gather"
94
+ end
95
+
96
+ def unified_reducers
97
+ unified_reducers = {}
98
+ @reducers.each do |reducer|
99
+ type = reducer["type"]
100
+ unified = unified_reducers[type]
101
+ if unified
102
+ unified["body"] = unified["body"].merge(reducer["body"])
103
+ unified["inputs"] += reducer["inputs"]
104
+ unified["outputs"] += reducer["outputs"]
105
+ else
106
+ unified_reducers[type] = reducer.dup
107
+ end
108
+ end
109
+ unified_reducers.values
110
+ end
111
+
112
+ def unified_gatherers
113
+ unified_gatherers = {}
114
+ @gatherers.each do |gatherer|
115
+ type = gatherer["type"]
116
+ unified = unified_gatherers[type]
117
+ if unified
118
+ unified["body"] = unified["body"].merge(gatherer["body"])
119
+ unified["inputs"] += gatherer["inputs"]
120
+ else
121
+ unified_gatherers[type] = gatherer.dup
122
+ end
123
+ end
124
+ unified_gatherers.values
125
+ end
126
+
127
+ def fixed_processor
128
+ @processor["outputs"] = @outputs
129
+ @processor
130
+ end
131
+
132
+ def reducer_message(command, name, reducer)
133
+ if reducer.is_a?(String)
134
+ reducer = {
135
+ "type" => reducer,
136
+ }
137
+ if reducer["type"] == REDUCE_SUM
138
+ reducer["limit"] = DEFAULT_LIMIT
139
+ end
140
+ end
141
+ {
142
+ "type" => command,
143
+ "body" => {
144
+ name => {
145
+ output_name(name) => reducer,
146
+ },
147
+ },
148
+ "inputs" => [name],
149
+ "outputs" => [output_name(name)],
150
+ }
151
+ end
152
+
153
+ def gatherer_message(command, name, gatherer=nil)
154
+ gatherer ||= {}
155
+ {
156
+ "type" => command,
157
+ "body" => {
158
+ output_name(name) => {
159
+ "output" => name,
160
+ }.merge(gatherer),
161
+ },
162
+ "inputs" => [output_name(name)],
163
+ "post" => true,
164
+ }
165
+ end
166
+
167
+ def output_name(name)
168
+ "#{name}_reduced"
169
+ end
170
+
171
+ #XXX Now, we include definitions to merge errors in the body.
172
+ # However, this makes the term "errors" reserved, so plugins
173
+ # cannot use their custom "errors" in the body.
174
+ # This must be rewritten.
175
+ def plan_errors_handling
176
+ reduce("errors"=> REDUCE_SUM)
177
+ end
178
+ end
179
+ end