switchman 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (642) hide show
  1. checksums.yaml +13 -5
  2. data/app/models/switchman/shard.rb +64 -19
  3. data/lib/switchman/action_controller/caching.rb +26 -0
  4. data/lib/switchman/active_record/abstract_adapter.rb +11 -0
  5. data/lib/switchman/active_record/association.rb +37 -3
  6. data/lib/switchman/active_record/attribute_methods.rb +3 -2
  7. data/lib/switchman/active_record/base.rb +44 -15
  8. data/lib/switchman/active_record/calculations.rb +145 -3
  9. data/lib/switchman/active_record/connection_handler.rb +11 -0
  10. data/lib/switchman/active_record/connection_pool.rb +5 -3
  11. data/lib/switchman/active_record/finder_methods.rb +25 -0
  12. data/lib/switchman/active_record/log_subscriber.rb +3 -2
  13. data/lib/switchman/active_record/query_cache.rb +71 -0
  14. data/lib/switchman/active_record/query_methods.rb +64 -29
  15. data/lib/switchman/active_record/relation.rb +44 -15
  16. data/lib/switchman/active_record/spawn_methods.rb +74 -0
  17. data/lib/switchman/active_support/cache.rb +17 -0
  18. data/lib/switchman/connection_pool_proxy.rb +47 -5
  19. data/lib/switchman/database_server.rb +131 -63
  20. data/lib/switchman/default_shard.rb +38 -23
  21. data/lib/switchman/engine.rb +71 -8
  22. data/lib/switchman/r_spec_helper.rb +41 -34
  23. data/lib/switchman/rails.rb +21 -0
  24. data/lib/switchman/shackles.rb +20 -20
  25. data/lib/switchman/sharded_instrumenter.rb +19 -0
  26. data/lib/switchman/test_helper.rb +3 -3
  27. data/lib/switchman/version.rb +1 -1
  28. data/lib/switchman.rb +1 -1
  29. data/spec/dummy/app/models/root.rb +5 -0
  30. data/spec/dummy/app/models/user.rb +6 -0
  31. data/spec/dummy/db/migrate/20140123154135_add_parent_id_to_users.rb +5 -0
  32. data/spec/dummy/db/migrate/20140219183820_create_roots.rb +9 -0
  33. data/spec/dummy/db/schema.rb +8 -1
  34. data/spec/dummy/log/development.log +135 -0
  35. data/spec/dummy/log/test.log +86777 -29398
  36. data/spec/dummy/tmp/cache/{317/9C0/shard%2F43 → 14A/870/key} +1 -1
  37. data/spec/dummy/tmp/cache/{319/A10/shard%2F72 → 2E9/8A0/shard%2F9} +1 -1
  38. data/spec/dummy/tmp/cache/317/9E0/shard%2F61 +0 -0
  39. data/spec/dummy/tmp/cache/318/9F0/shard%2F62 +0 -0
  40. data/spec/dummy/tmp/cache/{319/9F0/shard%2F54 → 318/A00/shard%2F71} +1 -1
  41. data/spec/dummy/tmp/cache/31A/A10/shard%2F64 +0 -0
  42. data/spec/dummy/tmp/cache/31A/A20/shard%2F73 +0 -0
  43. data/spec/dummy/tmp/cache/31B/A20/shard%2F65 +0 -0
  44. data/spec/dummy/tmp/cache/31B/A30/shard%2F74 +0 -0
  45. data/spec/dummy/tmp/cache/{31B/9E0/shard%2F29 → 31C/A40/shard%2F75} +1 -1
  46. data/spec/dummy/tmp/cache/31D/A30/shard%2F58 +0 -0
  47. data/spec/dummy/tmp/cache/31D/A40/shard%2F67 +0 -0
  48. data/spec/dummy/tmp/cache/31E/A40/shard%2F59 +0 -0
  49. data/spec/dummy/tmp/cache/31E/A50/shard%2F68 +0 -0
  50. data/spec/dummy/tmp/cache/320/A80/shard%2F79 +0 -0
  51. data/spec/dummy/tmp/cache/343/D90/shard%2F210 +0 -0
  52. data/spec/dummy/tmp/cache/344/D70/shard%2F103 +0 -0
  53. data/spec/dummy/tmp/cache/345/D80/shard%2F104 +0 -0
  54. data/spec/dummy/tmp/cache/345/DB0/shard%2F212 +0 -0
  55. data/spec/dummy/tmp/cache/345/DC0/shard%2F140 +0 -0
  56. data/spec/dummy/tmp/cache/345/DC0/shard%2F302 +0 -0
  57. data/spec/dummy/tmp/cache/346/D90/shard%2F105 +0 -0
  58. data/spec/dummy/tmp/cache/346/DD0/shard%2F303 +0 -0
  59. data/spec/dummy/tmp/cache/346/DF0/shard%2F321 +0 -0
  60. data/spec/dummy/tmp/cache/347/E00/shard%2F322 +0 -0
  61. data/spec/dummy/tmp/cache/347/E50/shard%2F610 +0 -0
  62. data/spec/dummy/tmp/cache/348/DD0/shard%2F206 +0 -0
  63. data/spec/dummy/tmp/cache/348/DE0/shard%2F215 +0 -0
  64. data/spec/dummy/tmp/cache/{316/980/shard%2F15 → 348/E10/shard%2F323} +1 -1
  65. data/spec/dummy/tmp/cache/348/E30/shard%2F422 +0 -0
  66. data/spec/dummy/tmp/cache/348/E40/shard%2F350 +0 -0
  67. data/spec/dummy/tmp/cache/348/E50/shard%2F602 +0 -0
  68. data/spec/dummy/tmp/cache/348/E80/shard%2F710 +0 -0
  69. data/spec/dummy/tmp/cache/348/E90/shard%2F800 +0 -0
  70. data/spec/dummy/tmp/cache/349/DE0/shard%2F126 +0 -0
  71. data/spec/dummy/tmp/cache/349/DF0/shard%2F216 +0 -0
  72. data/spec/dummy/tmp/cache/349/E00/shard%2F225 +0 -0
  73. data/spec/dummy/tmp/cache/349/E20/shard%2F243 +0 -0
  74. data/spec/dummy/tmp/cache/349/E20/shard%2F405 +0 -0
  75. data/spec/dummy/tmp/cache/349/E30/shard%2F333 +0 -0
  76. data/spec/dummy/tmp/cache/349/E40/shard%2F423 +0 -0
  77. data/spec/dummy/tmp/cache/349/E60/shard%2F603 +0 -0
  78. data/spec/dummy/tmp/cache/34A/DF0/shard%2F127 +0 -0
  79. data/spec/dummy/tmp/cache/34A/E10/shard%2F226 +0 -0
  80. data/spec/dummy/tmp/cache/34A/E30/shard%2F244 +0 -0
  81. data/spec/dummy/tmp/cache/34A/E50/shard%2F505 +0 -0
  82. data/spec/dummy/tmp/cache/34A/E60/shard%2F433 +0 -0
  83. data/spec/dummy/tmp/cache/34A/E70/shard%2F523 +0 -0
  84. data/spec/dummy/tmp/cache/34A/E80/shard%2F613 +0 -0
  85. data/spec/dummy/tmp/cache/34A/EA0/shard%2F712 +0 -0
  86. data/spec/dummy/tmp/cache/34A/EC0/shard%2F730 +0 -0
  87. data/spec/dummy/tmp/cache/34B/E00/shard%2F128 +1 -0
  88. data/spec/dummy/tmp/cache/34B/E00/shard%2F209 +0 -0
  89. data/spec/dummy/tmp/cache/34B/E10/shard%2F137 +0 -0
  90. data/spec/dummy/tmp/cache/34B/E30/shard%2F236 +0 -0
  91. data/spec/dummy/tmp/cache/34B/E50/shard%2F254 +0 -0
  92. data/spec/dummy/tmp/cache/34B/E60/shard%2F344 +0 -0
  93. data/spec/dummy/tmp/cache/34B/E60/shard%2F506 +0 -0
  94. data/spec/dummy/tmp/cache/34B/E80/shard%2F605 +0 -0
  95. data/spec/dummy/tmp/cache/34B/E90/shard%2F290 +0 -0
  96. data/spec/dummy/tmp/cache/34B/EC0/shard%2F560 +0 -0
  97. data/spec/dummy/tmp/cache/34B/ED0/shard%2F731 +0 -0
  98. data/spec/dummy/tmp/cache/34B/ED0/shard%2F812 +0 -0
  99. data/spec/dummy/tmp/cache/34C/E20/shard%2F138 +0 -0
  100. data/spec/dummy/tmp/cache/34C/E20/shard%2F219 +1 -0
  101. data/spec/dummy/tmp/cache/34C/E30/shard%2F228 +0 -0
  102. data/spec/dummy/tmp/cache/34C/E40/shard%2F237 +0 -0
  103. data/spec/dummy/tmp/cache/34C/E40/shard%2F318 +0 -0
  104. data/spec/dummy/tmp/cache/34C/E50/shard%2F246 +1 -0
  105. data/spec/dummy/tmp/cache/34C/E60/shard%2F255 +0 -0
  106. data/spec/dummy/tmp/cache/34C/E80/shard%2F192 +0 -0
  107. data/spec/dummy/tmp/cache/34C/EA0/shard%2F372 +0 -0
  108. data/spec/dummy/tmp/cache/34C/EA0/shard%2F453 +0 -0
  109. data/spec/dummy/tmp/cache/34C/EB0/shard%2F462 +0 -0
  110. data/spec/dummy/tmp/cache/34C/ED0/shard%2F480 +0 -0
  111. data/spec/dummy/tmp/cache/34C/ED0/shard%2F561 +0 -0
  112. data/spec/dummy/tmp/cache/34C/EF0/shard%2F822 +0 -0
  113. data/spec/dummy/tmp/cache/34C/EF0/shard%2F903 +0 -0
  114. data/spec/dummy/tmp/cache/34C/F00/shard%2F912 +0 -0
  115. data/spec/dummy/tmp/cache/34D/E50/shard%2F319 +0 -0
  116. data/spec/dummy/tmp/cache/34D/E80/shard%2F508 +0 -0
  117. data/spec/dummy/tmp/cache/34D/E90/shard%2F355 +0 -0
  118. data/spec/dummy/tmp/cache/34D/EA0/shard%2F364 +0 -0
  119. data/spec/dummy/tmp/cache/34D/EB0/shard%2F373 +0 -0
  120. data/spec/dummy/tmp/cache/34D/EB0/shard%2F454 +0 -0
  121. data/spec/dummy/tmp/cache/34D/EB0/shard%2F616 +0 -0
  122. data/spec/dummy/tmp/cache/34D/EC0/shard%2F544 +0 -0
  123. data/spec/dummy/tmp/cache/34D/ED0/shard%2F391 +0 -0
  124. data/spec/dummy/tmp/cache/34D/ED0/shard%2F715 +0 -0
  125. data/spec/dummy/tmp/cache/34D/F00/shard%2F661 +0 -0
  126. data/spec/dummy/tmp/cache/34D/F10/shard%2F913 +0 -0
  127. data/spec/dummy/tmp/cache/34E/E80/shard%2F257 +0 -0
  128. data/spec/dummy/tmp/cache/34E/EA0/shard%2F275 +0 -0
  129. data/spec/dummy/tmp/cache/34E/EB0/shard%2F365 +0 -0
  130. data/spec/dummy/tmp/cache/34E/EC0/shard%2F617 +0 -0
  131. data/spec/dummy/tmp/cache/34E/ED0/shard%2F545 +0 -0
  132. data/spec/dummy/tmp/cache/34E/EE0/shard%2F635 +0 -0
  133. data/spec/dummy/tmp/cache/34E/EF0/shard%2F482 +1 -0
  134. data/spec/dummy/tmp/cache/34E/EF0/shard%2F644 +0 -0
  135. data/spec/dummy/tmp/cache/34E/F00/shard%2F491 +0 -0
  136. data/spec/dummy/tmp/cache/34E/F00/shard%2F734 +1 -0
  137. data/spec/dummy/tmp/cache/34E/F40/shard%2F932 +0 -0
  138. data/spec/dummy/tmp/cache/34E/F50/shard%2F941 +0 -0
  139. data/spec/dummy/tmp/cache/34F/E80/shard%2F249 +0 -0
  140. data/spec/dummy/tmp/cache/34F/EA0/shard%2F267 +0 -0
  141. data/spec/dummy/tmp/cache/34F/EB0/shard%2F276 +0 -0
  142. data/spec/dummy/tmp/cache/34F/ED0/shard%2F375 +0 -0
  143. data/spec/dummy/tmp/cache/34F/ED0/shard%2F456 +0 -0
  144. data/spec/dummy/tmp/cache/34F/EE0/shard%2F465 +0 -0
  145. data/spec/dummy/tmp/cache/34F/EF0/shard%2F636 +0 -0
  146. data/spec/dummy/tmp/cache/34F/F00/shard%2F645 +0 -0
  147. data/spec/dummy/tmp/cache/34F/F10/shard%2F573 +0 -0
  148. data/spec/dummy/tmp/cache/34F/F20/shard%2F906 +0 -0
  149. data/spec/dummy/tmp/cache/34F/F40/shard%2F681 +0 -0
  150. data/spec/dummy/tmp/cache/34F/F50/shard%2F771 +0 -0
  151. data/spec/dummy/tmp/cache/34F/F60/shard%2F780 +0 -0
  152. data/spec/dummy/tmp/cache/34F/F60/shard%2F861 +0 -0
  153. data/spec/dummy/tmp/cache/34F/F60/shard%2F942 +0 -0
  154. data/spec/dummy/tmp/cache/350/EC0/shard%2F358 +0 -0
  155. data/spec/dummy/tmp/cache/350/EE0/shard%2F376 +0 -0
  156. data/spec/dummy/tmp/cache/350/EE0/shard%2F457 +0 -0
  157. data/spec/dummy/tmp/cache/350/EE0/shard%2F538 +0 -0
  158. data/spec/dummy/tmp/cache/350/EE0/shard%2F619 +0 -0
  159. data/spec/dummy/tmp/cache/350/EF0/shard%2F385 +0 -0
  160. data/spec/dummy/tmp/cache/350/EF0/shard%2F466 +0 -0
  161. data/spec/dummy/tmp/cache/350/EF0/shard%2F709 +0 -0
  162. data/spec/dummy/tmp/cache/350/F00/shard%2F394 +0 -0
  163. data/spec/dummy/tmp/cache/350/F20/shard%2F574 +0 -0
  164. data/spec/dummy/tmp/cache/350/F20/shard%2F736 +0 -0
  165. data/spec/dummy/tmp/cache/350/F30/shard%2F664 +0 -0
  166. data/spec/dummy/tmp/cache/350/F30/shard%2F745 +0 -0
  167. data/spec/dummy/tmp/cache/350/F50/shard%2F763 +0 -0
  168. data/spec/dummy/tmp/cache/350/F60/shard%2F772 +0 -0
  169. data/spec/dummy/tmp/cache/350/F70/shard%2F781 +0 -0
  170. data/spec/dummy/tmp/cache/350/F70/shard%2F862 +0 -0
  171. data/spec/dummy/tmp/cache/351/ED0/shard%2F359 +0 -0
  172. data/spec/dummy/tmp/cache/351/EF0/shard%2F296 +0 -0
  173. data/spec/dummy/tmp/cache/351/F00/shard%2F386 +0 -0
  174. data/spec/dummy/tmp/cache/351/F10/shard%2F476 +0 -0
  175. data/spec/dummy/tmp/cache/351/F10/shard%2F557 +0 -0
  176. data/spec/dummy/tmp/cache/351/F20/shard%2F485 +0 -0
  177. data/spec/dummy/tmp/cache/351/F30/shard%2F494 +0 -0
  178. data/spec/dummy/tmp/cache/351/F30/shard%2F737 +0 -0
  179. data/spec/dummy/tmp/cache/351/F40/shard%2F665 +0 -0
  180. data/spec/dummy/tmp/cache/351/F40/shard%2F746 +0 -0
  181. data/spec/dummy/tmp/cache/351/F70/shard%2F935 +0 -0
  182. data/spec/dummy/tmp/cache/352/F00/shard%2F297 +0 -0
  183. data/spec/dummy/tmp/cache/352/F00/shard%2F378 +0 -0
  184. data/spec/dummy/tmp/cache/352/F00/shard%2F459 +0 -0
  185. data/spec/dummy/tmp/cache/352/F10/shard%2F468 +1 -0
  186. data/spec/dummy/tmp/cache/352/F20/shard%2F639 +1 -0
  187. data/spec/dummy/tmp/cache/352/F30/shard%2F486 +0 -0
  188. data/spec/dummy/tmp/cache/352/F50/shard%2F585 +0 -0
  189. data/spec/dummy/tmp/cache/352/F50/shard%2F909 +0 -0
  190. data/spec/dummy/tmp/cache/352/F60/shard%2F594 +0 -0
  191. data/spec/dummy/tmp/cache/352/F70/shard%2F684 +0 -0
  192. data/spec/dummy/tmp/cache/352/F80/shard%2F774 +0 -0
  193. data/spec/dummy/tmp/cache/352/F80/shard%2F936 +0 -0
  194. data/spec/dummy/tmp/cache/352/F90/shard%2F783 +0 -0
  195. data/spec/dummy/tmp/cache/352/F90/shard%2F864 +0 -0
  196. data/spec/dummy/tmp/cache/352/F90/shard%2F945 +1 -0
  197. data/spec/dummy/tmp/cache/352/FA0/shard%2F873 +0 -0
  198. data/spec/dummy/tmp/cache/352/FC0/shard%2F972 +0 -0
  199. data/spec/dummy/tmp/cache/353/F50/shard%2F577 +1 -0
  200. data/spec/dummy/tmp/cache/353/F50/shard%2F739 +0 -0
  201. data/spec/dummy/tmp/cache/353/F60/shard%2F748 +0 -0
  202. data/spec/dummy/tmp/cache/353/F70/shard%2F595 +0 -0
  203. data/spec/dummy/tmp/cache/353/F90/shard%2F775 +0 -0
  204. data/spec/dummy/tmp/cache/353/FA0/shard%2F784 +0 -0
  205. data/spec/dummy/tmp/cache/353/FD0/shard%2F892 +0 -0
  206. data/spec/dummy/tmp/cache/354/000/shard%2F992 +0 -0
  207. data/spec/dummy/tmp/cache/354/F30/shard%2F389 +1 -0
  208. data/spec/dummy/tmp/cache/354/F40/shard%2F479 +0 -0
  209. data/spec/dummy/tmp/cache/354/F50/shard%2F488 +0 -0
  210. data/spec/dummy/tmp/cache/354/F90/shard%2F929 +0 -0
  211. data/spec/dummy/tmp/cache/354/FA0/shard%2F938 +0 -0
  212. data/spec/dummy/tmp/cache/354/FD0/shard%2F965 +0 -0
  213. data/spec/dummy/tmp/cache/354/FE0/shard%2F893 +0 -0
  214. data/spec/dummy/tmp/cache/354/FF0/shard%2F983 +0 -0
  215. data/spec/dummy/tmp/cache/355/010/shard%2F993 +0 -0
  216. data/spec/dummy/tmp/cache/355/F60/shard%2F489 +0 -0
  217. data/spec/dummy/tmp/cache/355/F80/shard%2F588 +0 -0
  218. data/spec/dummy/tmp/cache/355/F90/shard%2F678 +0 -0
  219. data/spec/dummy/tmp/cache/355/FA0/shard%2F687 +0 -0
  220. data/spec/dummy/tmp/cache/355/FB0/shard%2F777 +1 -0
  221. data/spec/dummy/tmp/cache/355/FB0/shard%2F858 +0 -0
  222. data/spec/dummy/tmp/cache/355/FB0/shard%2F939 +0 -0
  223. data/spec/dummy/tmp/cache/355/FD0/shard%2F795 +0 -0
  224. data/spec/dummy/tmp/cache/355/FD0/shard%2F876 +0 -0
  225. data/spec/dummy/tmp/cache/355/FE0/shard%2F966 +0 -0
  226. data/spec/dummy/tmp/cache/356/000/shard%2F895 +1 -0
  227. data/spec/dummy/tmp/cache/356/FA0/shard%2F679 +0 -0
  228. data/spec/dummy/tmp/cache/356/FB0/shard%2F688 +0 -0
  229. data/spec/dummy/tmp/cache/356/FC0/shard%2F859 +0 -0
  230. data/spec/dummy/tmp/cache/356/FE0/shard%2F796 +0 -0
  231. data/spec/dummy/tmp/cache/356/FE0/shard%2F877 +0 -0
  232. data/spec/dummy/tmp/cache/357/020/shard%2F986 +0 -0
  233. data/spec/dummy/tmp/cache/357/FC0/shard%2F689 +1 -0
  234. data/spec/dummy/tmp/cache/358/000/shard%2F879 +0 -0
  235. data/spec/dummy/tmp/cache/358/FF0/shard%2F789 +0 -0
  236. data/spec/dummy/tmp/cache/372/470/shard%2F1010 +0 -0
  237. data/spec/dummy/tmp/cache/373/4A0/shard%2F1110 +0 -0
  238. data/spec/dummy/tmp/cache/374/490/shard%2F1012 +1 -0
  239. data/spec/dummy/tmp/cache/375/4C0/shard%2F2003 +1 -0
  240. data/spec/dummy/tmp/cache/375/510/shard%2F1400 +0 -0
  241. data/spec/dummy/tmp/cache/376/4E0/shard%2F1041 +0 -0
  242. data/spec/dummy/tmp/cache/376/4E0/shard%2F1203 +0 -0
  243. data/spec/dummy/tmp/cache/376/4F0/shard%2F1212 +0 -0
  244. data/spec/dummy/tmp/cache/376/500/shard%2F1221 +0 -0
  245. data/spec/dummy/tmp/cache/377/4C0/shard%2F1015 +0 -0
  246. data/spec/dummy/tmp/cache/377/500/shard%2F1213 +0 -0
  247. data/spec/dummy/tmp/cache/377/530/shard%2F1240 +0 -0
  248. data/spec/dummy/tmp/cache/377/530/shard%2F1321 +0 -0
  249. data/spec/dummy/tmp/cache/377/530/shard%2F1402 +0 -0
  250. data/spec/dummy/tmp/cache/378/4E0/shard%2F1106 +0 -0
  251. data/spec/dummy/tmp/cache/378/510/shard%2F1133 +0 -0
  252. data/spec/dummy/tmp/cache/378/520/shard%2F1142 +0 -0
  253. data/spec/dummy/tmp/cache/378/530/shard%2F1313 +0 -0
  254. data/spec/dummy/tmp/cache/378/540/shard%2F1241 +0 -0
  255. data/spec/dummy/tmp/cache/378/540/shard%2F1403 +0 -0
  256. data/spec/dummy/tmp/cache/378/550/shard%2F1331 +0 -0
  257. data/spec/dummy/tmp/cache/378/560/shard%2F1340 +0 -0
  258. data/spec/dummy/tmp/cache/379/4F0/shard%2F1026 +0 -0
  259. data/spec/dummy/tmp/cache/379/500/shard%2F1035 +0 -0
  260. data/spec/dummy/tmp/cache/379/510/shard%2F1044 +0 -0
  261. data/spec/dummy/tmp/cache/379/520/shard%2F1134 +0 -0
  262. data/spec/dummy/tmp/cache/379/530/shard%2F1224 +0 -0
  263. data/spec/dummy/tmp/cache/379/550/shard%2F1080 +0 -0
  264. data/spec/dummy/tmp/cache/379/550/shard%2F1161 +0 -0
  265. data/spec/dummy/tmp/cache/379/550/shard%2F1404 +1 -0
  266. data/spec/dummy/tmp/cache/379/560/shard%2F1332 +0 -0
  267. data/spec/dummy/tmp/cache/379/570/shard%2F1260 +0 -0
  268. data/spec/dummy/tmp/cache/379/570/shard%2F1341 +0 -0
  269. data/spec/dummy/tmp/cache/379/5A0/shard%2F1611 +1 -0
  270. data/spec/dummy/tmp/cache/37A/4E0/shard%2F1009 +0 -0
  271. data/spec/dummy/tmp/cache/37A/4F0/shard%2F1018 +0 -0
  272. data/spec/dummy/tmp/cache/37A/500/shard%2F1027 +0 -0
  273. data/spec/dummy/tmp/cache/37A/510/shard%2F1117 +0 -0
  274. data/spec/dummy/tmp/cache/37A/520/shard%2F1045 +0 -0
  275. data/spec/dummy/tmp/cache/37A/570/shard%2F1252 +0 -0
  276. data/spec/dummy/tmp/cache/37A/580/shard%2F1261 +0 -0
  277. data/spec/dummy/tmp/cache/37A/5C0/shard%2F1702 +0 -0
  278. data/spec/dummy/tmp/cache/37A/5D0/shard%2F1711 +1 -0
  279. data/spec/dummy/tmp/cache/37B/500/shard%2F1019 +0 -0
  280. data/spec/dummy/tmp/cache/37B/510/shard%2F1028 +1 -0
  281. data/spec/dummy/tmp/cache/37B/510/shard%2F1109 +0 -0
  282. data/spec/dummy/tmp/cache/37B/540/shard%2F1136 +0 -0
  283. data/spec/dummy/tmp/cache/37B/580/shard%2F1253 +0 -0
  284. data/spec/dummy/tmp/cache/37B/590/shard%2F1181 +0 -0
  285. data/spec/dummy/tmp/cache/37B/5A0/shard%2F1190 +0 -0
  286. data/spec/dummy/tmp/cache/37B/5B0/shard%2F1523 +0 -0
  287. data/spec/dummy/tmp/cache/37B/5C0/shard%2F1370 +0 -0
  288. data/spec/dummy/tmp/cache/37B/600/shard%2F1811 +1 -0
  289. data/spec/dummy/tmp/cache/37C/530/shard%2F1038 +0 -0
  290. data/spec/dummy/tmp/cache/37C/550/shard%2F1137 +0 -0
  291. data/spec/dummy/tmp/cache/37C/560/shard%2F1065 +0 -0
  292. data/spec/dummy/tmp/cache/37C/580/shard%2F1164 +0 -0
  293. data/spec/dummy/tmp/cache/37C/590/shard%2F1092 +0 -0
  294. data/spec/dummy/tmp/cache/37C/590/shard%2F1335 +1 -0
  295. data/spec/dummy/tmp/cache/37C/5A0/shard%2F1182 +0 -0
  296. data/spec/dummy/tmp/cache/37C/5A0/shard%2F1263 +0 -0
  297. data/spec/dummy/tmp/cache/37C/5B0/shard%2F1191 +1 -0
  298. data/spec/dummy/tmp/cache/37C/5B0/shard%2F1434 +0 -0
  299. data/spec/dummy/tmp/cache/37C/5C0/shard%2F1605 +1 -0
  300. data/spec/dummy/tmp/cache/37C/5F0/shard%2F1470 +0 -0
  301. data/spec/dummy/tmp/cache/37C/620/shard%2F1821 +0 -0
  302. data/spec/dummy/tmp/cache/37C/620/shard%2F1902 +1 -0
  303. data/spec/dummy/tmp/cache/37C/640/shard%2F1920 +1 -0
  304. data/spec/dummy/tmp/cache/37D/540/shard%2F1039 +0 -0
  305. data/spec/dummy/tmp/cache/37D/560/shard%2F1057 +0 -0
  306. data/spec/dummy/tmp/cache/37D/590/shard%2F1246 +0 -0
  307. data/spec/dummy/tmp/cache/37D/590/shard%2F1408 +0 -0
  308. data/spec/dummy/tmp/cache/37D/5A0/shard%2F1093 +0 -0
  309. data/spec/dummy/tmp/cache/37D/5E0/shard%2F1372 +0 -0
  310. data/spec/dummy/tmp/cache/37D/5F0/shard%2F1381 +0 -0
  311. data/spec/dummy/tmp/cache/37D/610/shard%2F1642 +0 -0
  312. data/spec/dummy/tmp/cache/37D/610/shard%2F1804 +0 -0
  313. data/spec/dummy/tmp/cache/37E/570/shard%2F1058 +0 -0
  314. data/spec/dummy/tmp/cache/37E/570/shard%2F1139 +0 -0
  315. data/spec/dummy/tmp/cache/37E/580/shard%2F1229 +0 -0
  316. data/spec/dummy/tmp/cache/37E/5A0/shard%2F1247 +0 -0
  317. data/spec/dummy/tmp/cache/37E/5A0/shard%2F1409 +0 -0
  318. data/spec/dummy/tmp/cache/37E/5C0/shard%2F1346 +0 -0
  319. data/spec/dummy/tmp/cache/37E/5F0/shard%2F1292 +0 -0
  320. data/spec/dummy/tmp/cache/37E/5F0/shard%2F1373 +0 -0
  321. data/spec/dummy/tmp/cache/37E/5F0/shard%2F1535 +0 -0
  322. data/spec/dummy/tmp/cache/37E/600/shard%2F1382 +0 -0
  323. data/spec/dummy/tmp/cache/37E/620/shard%2F1805 +0 -0
  324. data/spec/dummy/tmp/cache/37E/640/shard%2F1661 +1 -0
  325. data/spec/dummy/tmp/cache/37F/5A0/shard%2F1158 +0 -0
  326. data/spec/dummy/tmp/cache/37F/5B0/shard%2F1167 +0 -0
  327. data/spec/dummy/tmp/cache/37F/5B0/shard%2F1248 +1 -0
  328. data/spec/dummy/tmp/cache/37F/5C0/shard%2F1095 +1 -0
  329. data/spec/dummy/tmp/cache/37F/5E0/shard%2F1275 +0 -0
  330. data/spec/dummy/tmp/cache/37F/5E0/shard%2F1437 +0 -0
  331. data/spec/dummy/tmp/cache/37F/600/shard%2F1455 +0 -0
  332. data/spec/dummy/tmp/cache/37F/600/shard%2F1536 +0 -0
  333. data/spec/dummy/tmp/cache/37F/610/shard%2F1464 +0 -0
  334. data/spec/dummy/tmp/cache/37F/620/shard%2F1473 +1 -0
  335. data/spec/dummy/tmp/cache/37F/630/shard%2F1482 +0 -0
  336. data/spec/dummy/tmp/cache/37F/640/shard%2F1491 +0 -0
  337. data/spec/dummy/tmp/cache/37F/640/shard%2F1653 +0 -0
  338. data/spec/dummy/tmp/cache/380/5C0/shard%2F1168 +0 -0
  339. data/spec/dummy/tmp/cache/380/5E0/shard%2F1429 +0 -0
  340. data/spec/dummy/tmp/cache/380/5F0/shard%2F1195 +0 -0
  341. data/spec/dummy/tmp/cache/380/5F0/shard%2F1276 +0 -0
  342. data/spec/dummy/tmp/cache/380/610/shard%2F1375 +0 -0
  343. data/spec/dummy/tmp/cache/380/620/shard%2F1384 +0 -0
  344. data/spec/dummy/tmp/cache/380/620/shard%2F1465 +0 -0
  345. data/spec/dummy/tmp/cache/380/640/shard%2F1645 +1 -0
  346. data/spec/dummy/tmp/cache/380/640/shard%2F1726 +1 -0
  347. data/spec/dummy/tmp/cache/380/650/shard%2F1492 +0 -0
  348. data/spec/dummy/tmp/cache/380/650/shard%2F1654 +0 -0
  349. data/spec/dummy/tmp/cache/380/660/shard%2F1906 +0 -0
  350. data/spec/dummy/tmp/cache/380/670/shard%2F1591 +0 -0
  351. data/spec/dummy/tmp/cache/380/670/shard%2F1753 +0 -0
  352. data/spec/dummy/tmp/cache/381/5F0/shard%2F1349 +0 -0
  353. data/spec/dummy/tmp/cache/381/600/shard%2F1196 +0 -0
  354. data/spec/dummy/tmp/cache/381/610/shard%2F1286 +0 -0
  355. data/spec/dummy/tmp/cache/381/620/shard%2F1295 +0 -0
  356. data/spec/dummy/tmp/cache/381/630/shard%2F1547 +1 -0
  357. data/spec/dummy/tmp/cache/381/650/shard%2F1565 +1 -0
  358. data/spec/dummy/tmp/cache/381/660/shard%2F1493 +1 -0
  359. data/spec/dummy/tmp/cache/381/670/shard%2F1745 +1 -0
  360. data/spec/dummy/tmp/cache/381/670/shard%2F1907 +0 -0
  361. data/spec/dummy/tmp/cache/381/680/shard%2F1592 +0 -0
  362. data/spec/dummy/tmp/cache/381/680/shard%2F1754 +0 -0
  363. data/spec/dummy/tmp/cache/381/6C0/shard%2F1790 +1 -0
  364. data/spec/dummy/tmp/cache/382/5F0/shard%2F1098 +0 -0
  365. data/spec/dummy/tmp/cache/382/600/shard%2F1269 +0 -0
  366. data/spec/dummy/tmp/cache/382/610/shard%2F1278 +1 -0
  367. data/spec/dummy/tmp/cache/382/620/shard%2F1287 +0 -0
  368. data/spec/dummy/tmp/cache/382/630/shard%2F1296 +0 -0
  369. data/spec/dummy/tmp/cache/382/640/shard%2F1467 +1 -0
  370. data/spec/dummy/tmp/cache/382/650/shard%2F1557 +0 -0
  371. data/spec/dummy/tmp/cache/382/690/shard%2F1674 +1 -0
  372. data/spec/dummy/tmp/cache/382/6A0/shard%2F1845 +1 -0
  373. data/spec/dummy/tmp/cache/382/6D0/shard%2F1953 +1 -0
  374. data/spec/dummy/tmp/cache/382/700/shard%2F1980 +0 -0
  375. data/spec/dummy/tmp/cache/383/610/shard%2F1189 +0 -0
  376. data/spec/dummy/tmp/cache/383/630/shard%2F1369 +0 -0
  377. data/spec/dummy/tmp/cache/383/640/shard%2F1378 +0 -0
  378. data/spec/dummy/tmp/cache/383/660/shard%2F1639 +1 -0
  379. data/spec/dummy/tmp/cache/383/680/shard%2F1576 +1 -0
  380. data/spec/dummy/tmp/cache/383/690/shard%2F1828 +1 -0
  381. data/spec/dummy/tmp/cache/383/6C0/shard%2F1774 +1 -0
  382. data/spec/dummy/tmp/cache/383/700/shard%2F1891 +0 -0
  383. data/spec/dummy/tmp/cache/383/700/shard%2F1972 +1 -0
  384. data/spec/dummy/tmp/cache/384/6B0/shard%2F1838 +0 -0
  385. data/spec/dummy/tmp/cache/384/700/shard%2F1883 +1 -0
  386. data/spec/dummy/tmp/cache/384/710/shard%2F1892 +0 -0
  387. data/spec/dummy/tmp/cache/386/690/shard%2F1399 +0 -0
  388. data/spec/dummy/tmp/cache/386/6D0/shard%2F1759 +1 -0
  389. data/spec/dummy/tmp/cache/386/6F0/shard%2F1858 +1 -0
  390. data/spec/dummy/tmp/cache/386/750/shard%2F1993 +1 -0
  391. data/spec/dummy/tmp/cache/388/710/shard%2F1698 +1 -0
  392. data/spec/dummy/tmp/cache/3A3/EA0/shard%2F10101 +1 -0
  393. data/spec/dummy/tmp/cache/3A3/EC0/shard%2F11010 +1 -0
  394. data/spec/dummy/tmp/cache/3A6/EB0/shard%2F10005 +1 -0
  395. data/spec/dummy/tmp/cache/3A6/ED0/shard%2F10104 +1 -0
  396. data/spec/dummy/tmp/cache/3A7/EC0/shard%2F10006 +1 -0
  397. data/spec/dummy/tmp/cache/3A7/F10/shard%2F10051 +1 -0
  398. data/spec/dummy/tmp/cache/3A8/F50/shard%2F10322 +1 -0
  399. data/spec/dummy/tmp/cache/3A9/F10/shard%2F10035 +1 -0
  400. data/spec/dummy/tmp/cache/3A9/F90/shard%2F11403 +1 -0
  401. data/spec/dummy/tmp/cache/3AA/F40/shard%2F10216 +1 -0
  402. data/spec/dummy/tmp/cache/3AA/FB0/shard%2F11332 +1 -0
  403. data/spec/dummy/tmp/cache/3AB/030/shard%2F11720 +1 -0
  404. data/spec/dummy/tmp/cache/3AB/F10/shard%2F10019 +1 -0
  405. data/spec/dummy/tmp/cache/3AB/F40/shard%2F10127 +1 -0
  406. data/spec/dummy/tmp/cache/3AB/F50/shard%2F11027 +1 -0
  407. data/spec/dummy/tmp/cache/3AB/F80/shard%2F10244 +1 -0
  408. data/spec/dummy/tmp/cache/3AC/020/shard%2F10731 +1 -0
  409. data/spec/dummy/tmp/cache/3AC/020/shard%2F11541 +1 -0
  410. data/spec/dummy/tmp/cache/3AC/020/shard%2F11703 +1 -0
  411. data/spec/dummy/tmp/cache/3AC/F80/shard%2F10074 +1 -0
  412. data/spec/dummy/tmp/cache/3AC/F90/shard%2F10083 +1 -0
  413. data/spec/dummy/tmp/cache/3AC/FA0/shard%2F10173 +1 -0
  414. data/spec/dummy/tmp/cache/3AC/FF0/shard%2F11190 +1 -0
  415. data/spec/dummy/tmp/cache/3AD/020/shard%2F10480 +1 -0
  416. data/spec/dummy/tmp/cache/3AD/F70/shard%2F10138 +1 -0
  417. data/spec/dummy/tmp/cache/3AE/050/shard%2F11471 +1 -0
  418. data/spec/dummy/tmp/cache/3AE/080/shard%2F11822 +1 -0
  419. data/spec/dummy/tmp/cache/3AE/F90/shard%2F10067 +1 -0
  420. data/spec/dummy/tmp/cache/3AE/FF0/shard%2F11093 +1 -0
  421. data/spec/dummy/tmp/cache/3AF/070/shard%2F11643 +1 -0
  422. data/spec/dummy/tmp/cache/3AF/070/shard%2F11805 +1 -0
  423. data/spec/dummy/tmp/cache/3AF/FA0/shard%2F10068 +1 -0
  424. data/spec/dummy/tmp/cache/3AF/FF0/shard%2F11247 +1 -0
  425. data/spec/dummy/tmp/cache/3B0/040/shard%2F10636 +1 -0
  426. data/spec/dummy/tmp/cache/3B0/060/shard%2F11626 +1 -0
  427. data/spec/dummy/tmp/cache/3B0/0B0/shard%2F10942 +1 -0
  428. data/spec/dummy/tmp/cache/3B0/FC0/shard%2F10159 +1 -0
  429. data/spec/dummy/tmp/cache/3B1/040/shard%2F10466 +1 -0
  430. data/spec/dummy/tmp/cache/3B1/040/shard%2F11276 +1 -0
  431. data/spec/dummy/tmp/cache/3B1/050/shard%2F11609 +1 -0
  432. data/spec/dummy/tmp/cache/3B1/070/shard%2F10574 +1 -0
  433. data/spec/dummy/tmp/cache/3B1/070/shard%2F11465 +1 -0
  434. data/spec/dummy/tmp/cache/3B1/0A0/shard%2F11492 +1 -0
  435. data/spec/dummy/tmp/cache/3B1/0A0/shard%2F11654 +1 -0
  436. data/spec/dummy/tmp/cache/3B1/0E0/shard%2F11771 +1 -0
  437. data/spec/dummy/tmp/cache/3B2/050/shard%2F10386 +1 -0
  438. data/spec/dummy/tmp/cache/3B2/0C0/shard%2F11907 +1 -0
  439. data/spec/dummy/tmp/cache/3B2/0D0/shard%2F11592 +1 -0
  440. data/spec/dummy/tmp/cache/3B2/0D0/shard%2F11754 +1 -0
  441. data/spec/dummy/tmp/cache/3B2/110/shard%2F11952 +1 -0
  442. data/spec/dummy/tmp/cache/3B3/0C0/shard%2F11575 +1 -0
  443. data/spec/dummy/tmp/cache/3B3/0C0/shard%2F11737 +1 -0
  444. data/spec/dummy/tmp/cache/3B4/0B0/shard%2F11558 +1 -0
  445. data/spec/dummy/tmp/cache/3B4/0E0/shard%2F10775 +1 -0
  446. data/spec/dummy/tmp/cache/3B4/130/shard%2F11873 +1 -0
  447. data/spec/dummy/tmp/cache/3B4/160/shard%2F11981 +1 -0
  448. data/spec/dummy/tmp/cache/3B5/120/shard%2F11856 +1 -0
  449. data/spec/dummy/tmp/cache/3B5/130/shard%2F10893 +1 -0
  450. data/spec/dummy/tmp/cache/3B5/150/shard%2F11964 +1 -0
  451. data/spec/dummy/tmp/cache/3B5/160/shard%2F11892 +1 -0
  452. data/spec/dummy/tmp/cache/3B6/110/shard%2F11839 +1 -0
  453. data/spec/dummy/tmp/cache/3B6/120/shard%2F11686 +1 -0
  454. data/spec/dummy/tmp/cache/3B7/100/shard%2F10688 +1 -0
  455. data/spec/dummy/tmp/cache/3B7/110/shard%2F11669 +1 -0
  456. data/spec/dummy/tmp/cache/3B9/170/shard%2F11788 +1 -0
  457. data/spec/dummy/tmp/cache/3BC/1F0/shard%2F11998 +1 -0
  458. data/spec/dummy/tmp/cache/4B7/9C0/shard_12%3Akey +1 -0
  459. data/spec/dummy/tmp/cache/4B8/A30/shard_13%3Akey +1 -0
  460. data/spec/dummy/tmp/cache/4B9/AA0/shard_14%3Akey +1 -0
  461. data/spec/dummy/tmp/cache/4BA/B50/shard_51%3Akey +1 -0
  462. data/spec/dummy/tmp/cache/4BB/B90/shard_25%3Akey +1 -0
  463. data/spec/dummy/tmp/cache/4BB/BC0/shard_52%3Akey +1 -0
  464. data/spec/dummy/tmp/cache/4BC/C00/shard_26%3Akey +1 -0
  465. data/spec/dummy/tmp/cache/4BC/C50/shard_71%3Akey +1 -0
  466. data/spec/dummy/tmp/cache/4BD/CC0/shard_72%3Akey +1 -0
  467. data/spec/dummy/tmp/cache/4BD/CE0/shard_90%3Akey +1 -0
  468. data/spec/dummy/tmp/cache/4BE/D00/shard_46%3Akey +1 -0
  469. data/spec/dummy/tmp/cache/4BF/D70/shard_47%3Akey +1 -0
  470. data/spec/dummy/tmp/cache/4C0/DF0/shard_57%3Akey +1 -0
  471. data/spec/dummy/tmp/cache/4C0/E20/shard_84%3Akey +1 -0
  472. data/spec/dummy/tmp/cache/4C1/E60/shard_58%3Akey +1 -0
  473. data/spec/dummy/tmp/cache/4C1/E90/shard_85%3Akey +1 -0
  474. data/spec/dummy/tmp/cache/4C5/050/shard_89%3Akey +1 -0
  475. data/spec/dummy/tmp/cache/4E5/B10/shard_100%3Akey +1 -0
  476. data/spec/dummy/tmp/cache/4E6/B80/shard_101%3Akey +1 -0
  477. data/spec/dummy/tmp/cache/4E8/CA0/shard_220%3Akey +1 -0
  478. data/spec/dummy/tmp/cache/4E9/D10/shard_221%3Akey +1 -0
  479. data/spec/dummy/tmp/cache/4EA/D90/shard_150%3Akey +1 -0
  480. data/spec/dummy/tmp/cache/4EA/DC0/shard_420%3Akey +1 -0
  481. data/spec/dummy/tmp/cache/4EB/E00/shard_151%3Akey +1 -0
  482. data/spec/dummy/tmp/cache/4ED/EE0/shard_153%3Akey +1 -0
  483. data/spec/dummy/tmp/cache/4EE/F50/shard_154%3Akey +1 -0
  484. data/spec/dummy/tmp/cache/4EE/F80/shard_181%3Akey +1 -0
  485. data/spec/dummy/tmp/cache/4EE/F90/shard_352%3Akey +1 -0
  486. data/spec/dummy/tmp/cache/4EF/000/shard_272%3Akey +1 -0
  487. data/spec/dummy/tmp/cache/4EF/000/shard_353%3Akey +1 -0
  488. data/spec/dummy/tmp/cache/4EF/000/shard_515%3Akey +1 -0
  489. data/spec/dummy/tmp/cache/4EF/030/shard_380%3Akey +1 -0
  490. data/spec/dummy/tmp/cache/4EF/040/shard_632%3Akey +1 -0
  491. data/spec/dummy/tmp/cache/4EF/FF0/shard_182%3Akey +1 -0
  492. data/spec/dummy/tmp/cache/4F0/010/shard_138%3Akey +1 -0
  493. data/spec/dummy/tmp/cache/4F0/070/shard_273%3Akey +1 -0
  494. data/spec/dummy/tmp/cache/4F0/070/shard_516%3Akey +1 -0
  495. data/spec/dummy/tmp/cache/4F0/0A0/shard_381%3Akey +1 -0
  496. data/spec/dummy/tmp/cache/4F0/0B0/shard_633%3Akey +1 -0
  497. data/spec/dummy/tmp/cache/4F1/080/shard_139%3Akey +1 -0
  498. data/spec/dummy/tmp/cache/4F1/140/shard_814%3Akey +1 -0
  499. data/spec/dummy/tmp/cache/4F2/120/shard_167%3Akey +1 -0
  500. data/spec/dummy/tmp/cache/4F2/130/shard_419%3Akey +1 -0
  501. data/spec/dummy/tmp/cache/4F2/1B0/shard_815%3Akey +1 -0
  502. data/spec/dummy/tmp/cache/4F2/1E0/shard_923%3Akey +1 -0
  503. data/spec/dummy/tmp/cache/4F3/190/shard_168%3Akey +1 -0
  504. data/spec/dummy/tmp/cache/4F3/250/shard_924%3Akey +1 -0
  505. data/spec/dummy/tmp/cache/4F5/320/shard_755%3Akey +1 -0
  506. data/spec/dummy/tmp/cache/4F6/360/shard_567%3Akey +1 -0
  507. data/spec/dummy/tmp/cache/4F6/390/shard_756%3Akey +1 -0
  508. data/spec/dummy/tmp/cache/4F7/3D0/shard_568%3Akey +1 -0
  509. data/spec/dummy/tmp/cache/4F7/3E0/shard_658%3Akey +1 -0
  510. data/spec/dummy/tmp/cache/4F8/450/shard_659%3Akey +1 -0
  511. data/spec/dummy/tmp/cache/4F8/4C0/shard_884%3Akey +1 -0
  512. data/spec/dummy/tmp/cache/4F9/530/shard_885%3Akey +1 -0
  513. data/spec/dummy/tmp/cache/4FA/5A0/shard_967%3Akey +1 -0
  514. data/spec/dummy/tmp/cache/4FB/610/shard_968%3Akey +1 -0
  515. data/spec/dummy/tmp/cache/518/1C0/shard_1111%3Akey +1 -0
  516. data/spec/dummy/tmp/cache/519/230/shard_1112%3Akey +1 -0
  517. data/spec/dummy/tmp/cache/51A/2C0/shard_1050%3Akey +1 -0
  518. data/spec/dummy/tmp/cache/51B/3A0/shard_1600%3Akey +1 -0
  519. data/spec/dummy/tmp/cache/51C/410/shard_1601%3Akey +1 -0
  520. data/spec/dummy/tmp/cache/51D/4B0/shard_1710%3Akey +1 -0
  521. data/spec/dummy/tmp/cache/51E/4E0/shard_1351%3Akey +1 -0
  522. data/spec/dummy/tmp/cache/51F/550/shard_1352%3Akey +1 -0
  523. data/spec/dummy/tmp/cache/51F/570/shard_1532%3Akey +1 -0
  524. data/spec/dummy/tmp/cache/520/580/shard_1155%3Akey +1 -0
  525. data/spec/dummy/tmp/cache/520/5A0/shard_1416%3Akey +1 -0
  526. data/spec/dummy/tmp/cache/520/5D0/shard_1443%3Akey +1 -0
  527. data/spec/dummy/tmp/cache/520/5E0/shard_1452%3Akey +1 -0
  528. data/spec/dummy/tmp/cache/520/5E0/shard_1533%3Akey +1 -0
  529. data/spec/dummy/tmp/cache/520/5F0/shard_1461%3Akey +1 -0
  530. data/spec/dummy/tmp/cache/520/600/shard_1551%3Akey +1 -0
  531. data/spec/dummy/tmp/cache/521/5F0/shard_1156%3Akey +1 -0
  532. data/spec/dummy/tmp/cache/521/610/shard_1417%3Akey +1 -0
  533. data/spec/dummy/tmp/cache/521/620/shard_1507%3Akey +1 -0
  534. data/spec/dummy/tmp/cache/521/630/shard_1516%3Akey +1 -0
  535. data/spec/dummy/tmp/cache/521/640/shard_1444%3Akey +1 -0
  536. data/spec/dummy/tmp/cache/521/650/shard_1453%3Akey +1 -0
  537. data/spec/dummy/tmp/cache/521/660/shard_1462%3Akey +1 -0
  538. data/spec/dummy/tmp/cache/521/670/shard_1552%3Akey +1 -0
  539. data/spec/dummy/tmp/cache/521/680/shard_1480%3Akey +1 -0
  540. data/spec/dummy/tmp/cache/521/690/shard_1813%3Akey +1 -0
  541. data/spec/dummy/tmp/cache/522/630/shard_1049%3Akey +1 -0
  542. data/spec/dummy/tmp/cache/522/690/shard_1508%3Akey +1 -0
  543. data/spec/dummy/tmp/cache/522/6A0/shard_1517%3Akey +1 -0
  544. data/spec/dummy/tmp/cache/522/700/shard_1571%3Akey +1 -0
  545. data/spec/dummy/tmp/cache/522/700/shard_1733%3Akey +1 -0
  546. data/spec/dummy/tmp/cache/522/700/shard_1814%3Akey +1 -0
  547. data/spec/dummy/tmp/cache/522/740/shard_1850%3Akey +1 -0
  548. data/spec/dummy/tmp/cache/523/6F0/shard_1257%3Akey +1 -0
  549. data/spec/dummy/tmp/cache/523/730/shard_1617%3Akey +1 -0
  550. data/spec/dummy/tmp/cache/523/770/shard_1572%3Akey +1 -0
  551. data/spec/dummy/tmp/cache/523/770/shard_1734%3Akey +1 -0
  552. data/spec/dummy/tmp/cache/523/7B0/shard_1851%3Akey +1 -0
  553. data/spec/dummy/tmp/cache/524/760/shard_1258%3Akey +1 -0
  554. data/spec/dummy/tmp/cache/524/7A0/shard_1537%3Akey +1 -0
  555. data/spec/dummy/tmp/cache/524/7A0/shard_1618%3Akey +1 -0
  556. data/spec/dummy/tmp/cache/524/7C0/shard_1636%3Akey +1 -0
  557. data/spec/dummy/tmp/cache/524/820/shard_1690%3Akey +1 -0
  558. data/spec/dummy/tmp/cache/525/810/shard_1538%3Akey +1 -0
  559. data/spec/dummy/tmp/cache/525/820/shard_1709%3Akey +1 -0
  560. data/spec/dummy/tmp/cache/525/830/shard_1637%3Akey +1 -0
  561. data/spec/dummy/tmp/cache/525/870/shard_1835%3Akey +1 -0
  562. data/spec/dummy/tmp/cache/525/8A0/shard_1862%3Akey +1 -0
  563. data/spec/dummy/tmp/cache/526/8D0/shard_1665%3Akey +1 -0
  564. data/spec/dummy/tmp/cache/526/8E0/shard_1755%3Akey +1 -0
  565. data/spec/dummy/tmp/cache/526/8E0/shard_1836%3Akey +1 -0
  566. data/spec/dummy/tmp/cache/526/910/shard_1863%3Akey +1 -0
  567. data/spec/dummy/tmp/cache/527/8F0/shard_1297%3Akey +1 -0
  568. data/spec/dummy/tmp/cache/527/920/shard_1486%3Akey +1 -0
  569. data/spec/dummy/tmp/cache/527/930/shard_1738%3Akey +1 -0
  570. data/spec/dummy/tmp/cache/527/940/shard_1585%3Akey +1 -0
  571. data/spec/dummy/tmp/cache/527/940/shard_1666%3Akey +1 -0
  572. data/spec/dummy/tmp/cache/527/950/shard_1756%3Akey +1 -0
  573. data/spec/dummy/tmp/cache/527/990/shard_1792%3Akey +1 -0
  574. data/spec/dummy/tmp/cache/528/960/shard_1298%3Akey +1 -0
  575. data/spec/dummy/tmp/cache/528/990/shard_1487%3Akey +1 -0
  576. data/spec/dummy/tmp/cache/528/9A0/shard_1739%3Akey +1 -0
  577. data/spec/dummy/tmp/cache/528/9B0/shard_1586%3Akey +1 -0
  578. data/spec/dummy/tmp/cache/528/9E0/shard_1775%3Akey +1 -0
  579. data/spec/dummy/tmp/cache/528/A00/shard_1793%3Akey +1 -0
  580. data/spec/dummy/tmp/cache/529/9F0/shard_1479%3Akey +1 -0
  581. data/spec/dummy/tmp/cache/529/A30/shard_1677%3Akey +1 -0
  582. data/spec/dummy/tmp/cache/529/A50/shard_1776%3Akey +1 -0
  583. data/spec/dummy/tmp/cache/529/A80/shard_1884%3Akey +1 -0
  584. data/spec/dummy/tmp/cache/52A/A80/shard_1498%3Akey +1 -0
  585. data/spec/dummy/tmp/cache/52A/AA0/shard_1678%3Akey +1 -0
  586. data/spec/dummy/tmp/cache/52A/AF0/shard_1885%3Akey +1 -0
  587. data/spec/dummy/tmp/cache/52B/AF0/shard_1499%3Akey +1 -0
  588. data/spec/dummy/tmp/cache/52C/B90/shard_1689%3Akey +1 -0
  589. data/spec/dummy/tmp/cache/52E/CC0/shard_1898%3Akey +1 -0
  590. data/spec/dummy/tmp/cache/52F/D30/shard_1899%3Akey +1 -0
  591. data/spec/lib/action_controller/caching_spec.rb +49 -0
  592. data/spec/lib/active_record/association_spec.rb +41 -0
  593. data/spec/lib/active_record/attribute_methods_spec.rb +6 -0
  594. data/spec/lib/active_record/base_spec.rb +20 -4
  595. data/spec/lib/active_record/calculations_spec.rb +117 -0
  596. data/spec/lib/active_record/finder_methods_spec.rb +22 -0
  597. data/spec/lib/active_record/query_cache_spec.rb +288 -0
  598. data/spec/lib/active_record/query_methods_spec.rb +33 -0
  599. data/spec/lib/active_record/relation_spec.rb +9 -0
  600. data/spec/lib/active_record/spawn_methods_spec.rb +43 -0
  601. data/spec/lib/database_server_spec.rb +73 -14
  602. data/spec/lib/default_shard_spec.rb +24 -0
  603. data/spec/lib/r_spec_helper_spec.rb +20 -0
  604. data/spec/lib/rails_spec.rb +31 -0
  605. data/spec/lib/shackles_spec.rb +68 -14
  606. data/spec/models/shard_spec.rb +30 -9
  607. metadata +1132 -94
  608. data/lib/switchman/cache_extensions.rb +0 -12
  609. data/spec/dummy/tmp/cache/2E2/830/shard%2F2 +0 -0
  610. data/spec/dummy/tmp/cache/2E3/840/shard%2F3 +0 -0
  611. data/spec/dummy/tmp/cache/313/970/shard%2F30 +0 -0
  612. data/spec/dummy/tmp/cache/314/980/shard%2F31 +0 -0
  613. data/spec/dummy/tmp/cache/316/9D0/shard%2F60 +0 -0
  614. data/spec/dummy/tmp/cache/317/990/shard%2F16 +0 -0
  615. data/spec/dummy/tmp/cache/318/9A0/shard%2F17 +0 -0
  616. data/spec/dummy/tmp/cache/318/9D0/shard%2F44 +0 -0
  617. data/spec/dummy/tmp/cache/319/9E0/shard%2F45 +0 -0
  618. data/spec/dummy/tmp/cache/319/A30/shard%2F90 +0 -0
  619. data/spec/dummy/tmp/cache/321/AA0/shard%2F89 +0 -0
  620. data/spec/dummy/tmp/cache/322/AC0/shard%2F99 +0 -1
  621. data/spec/dummy/tmp/cache/345/DB0/shard%2F131 +0 -1
  622. data/spec/dummy/tmp/cache/346/DB0/shard%2F123 +0 -0
  623. data/spec/dummy/tmp/cache/346/DD0/shard%2F222 +0 -1
  624. data/spec/dummy/tmp/cache/346/DE0/shard%2F150 +0 -0
  625. data/spec/dummy/tmp/cache/346/DF0/shard%2F240 +0 -1
  626. data/spec/dummy/tmp/cache/347/DA0/shard%2F106 +0 -1
  627. data/spec/dummy/tmp/cache/347/DC0/shard%2F124 +0 -0
  628. data/spec/dummy/tmp/cache/347/DC0/shard%2F205 +0 -1
  629. data/spec/dummy/tmp/cache/347/E10/shard%2F250 +0 -1
  630. data/spec/dummy/tmp/cache/348/DF0/shard%2F143 +0 -1
  631. data/spec/dummy/tmp/cache/348/DF0/shard%2F224 +0 -1
  632. data/spec/dummy/tmp/cache/348/E10/shard%2F161 +0 -1
  633. data/spec/dummy/tmp/cache/349/DD0/shard%2F117 +0 -1
  634. data/spec/dummy/tmp/cache/349/E40/shard%2F180 +0 -1
  635. data/spec/dummy/tmp/cache/34A/DF0/shard%2F208 +0 -1
  636. data/spec/dummy/tmp/cache/34A/E10/shard%2F145 +0 -1
  637. data/spec/dummy/tmp/cache/34A/E60/shard%2F190 +0 -1
  638. data/spec/dummy/tmp/cache/34B/E30/shard%2F155 +0 -1
  639. data/spec/dummy/tmp/cache/34D/E30/shard%2F139 +0 -0
  640. data/spec/dummy/tmp/cache/34E/E50/shard%2F149 +0 -0
  641. data/spec/dummy/tmp/cache/353/EF0/shard%2F199 +0 -1
  642. data/spec/lib/cache_extensions_spec.rb +0 -27
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: cc14f7783fcf89a84b17781a64cc331aa29f0447
4
- data.tar.gz: 7921d4093ae4c1194b03724fa10013d9c71dbcae
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZGY5N2I3ZDdiMzViMzAzYmRjOTQxMGFjMmNmNjIwYzhjNTNiMDM2NQ==
5
+ data.tar.gz: !binary |-
6
+ NTc3ZGM5YTY5NTI3MTc4ODNiMjdlNDRmM2QwNjU5N2JmNzA2ZDg4Nw==
5
7
  SHA512:
6
- metadata.gz: ec8355813399e1a4d0f01c48c299498735ee1bd913fe2083624e161d26e32533c9d5a0695f0ddfa726648f63292fd9a5e1f58ecbdacfc6d0f70274d148cae52b
7
- data.tar.gz: b7f54166cf2d7eab085cfe199bd47be4d1566860de6805a67bc3614e86bc04eebef4d80b892b2c5a26a71d5604d5b80a62efb260deee6dbee6f7a8f5190acf34
8
+ metadata.gz: !binary |-
9
+ M2RjZDFiMzVhZDgyNzFhMTg2YjBmY2M2OTY0Nzg2OTc0NjkxZjkzOTdjYzU4
10
+ ZmE0OGY2YmI0OGUwM2FhOWFhYjFjMDk3MGI5OThmZDZlZWNiMWZjMTEzMDZl
11
+ ZDM4MmIyMTJhNDZlZTk1NDg3ODhiZjczMDYxNTQ1ZDE1NDY4ODU=
12
+ data.tar.gz: !binary |-
13
+ OTc4N2ZiNjNlY2YzMjk5NTMxN2ZhZWRiNzJmZDY0NWU0YmFmNGU0NzRlYmRi
14
+ YmRlMDcyMzY5MTU4ZWI3YWEwMDM1YTc1ZDZhYTAwMzU5OTY4MDMxZTNjYjE4
15
+ YWFlNGU5Y2ZlMzBhZGYwZDRjNjY3OTI5YThkZjc5NWFlNGQyMzA=
@@ -14,6 +14,7 @@ module Switchman
14
14
  :unsharded => [Shard]
15
15
  }
16
16
  private_constant :CATEGORIES
17
+ @shard_category = :unsharded
17
18
 
18
19
  attr_accessible :name, :database_server, :default
19
20
 
@@ -22,7 +23,6 @@ module Switchman
22
23
 
23
24
  after_save :clear_cache
24
25
 
25
-
26
26
  class << self
27
27
  def categories
28
28
  CATEGORIES.keys
@@ -35,10 +35,12 @@ module Switchman
35
35
  # is up and running everywhere else). This includes for looking up the
36
36
  # default shard itself. This also needs to be a local so that this method
37
37
  # can be re-entrant
38
- default = DefaultShard.new
38
+ default = DefaultShard.instance
39
39
 
40
40
  # the first time we need a dummy dummy for re-entrancy to avoid looping on ourselves
41
41
  @default ||= default
42
+ # forget our current shard activations - it might have "another" default shard serialized there
43
+ active_shards.clear
42
44
 
43
45
  # Now find the actual record, if it exists; rescue the fake default if the table doesn't exist
44
46
  @default = Shard.find_by_default(true) || default rescue default
@@ -77,7 +79,7 @@ module Switchman
77
79
  cached_shards[id] ||= Shard.default.activate do
78
80
  # can't simply cache the AR object since Shard has a custom serializer
79
81
  # that calls this method
80
- attributes = Rails.cache.fetch(['shard', id].join('/')) do
82
+ attributes = ::Rails.cache.fetch(['shard', id].join('/')) do
81
83
  shard = find_by_id(id)
82
84
  shard.try(:attributes) || :nil
83
85
  end
@@ -99,16 +101,35 @@ module Switchman
99
101
  @cached_shards = {}
100
102
  end
101
103
 
102
- # options
103
- # :parallel - true/false to execute in parallel, or a integer of how many
104
- # sub-processes per database server. Note that parallel
105
- # invocation currently uses forking, so should be used sparingly
106
- # because errors are not raised, and you cannot get results back
107
- def with_each_shard(scope = nil, categories = nil, options = {})
104
+ # ==== Parameters
105
+ #
106
+ # * +shards+ - an array or relation of Shards to iterate over
107
+ # * +categories+ - an array of categories to activate
108
+ # * +options+ -
109
+ # :parallel - true/false to execute in parallel, or a integer of how many
110
+ # sub-processes per database server. Note that parallel
111
+ # invocation currently uses forking, so should be used sparingly
112
+ # because errors are not raised, and you cannot get results back
113
+ # :exception - :ignore, :raise, :defer (wait until the end and raise the first
114
+ # error), or a proc
115
+ def with_each_shard(*args)
116
+ raise ArgumentError("wrong number of arguments (#{args.length} for 0...3)") if args.length > 3
117
+
108
118
  unless default.is_a?(Shard)
109
119
  return Array(yield)
110
120
  end
111
121
 
122
+ options = args.extract_options!
123
+ if args.length == 1
124
+ if Array === args.first && args.first.first.is_a?(Symbol)
125
+ categories = args.first
126
+ else
127
+ scope = args.first
128
+ end
129
+ else
130
+ scope, categories = args
131
+ end
132
+
112
133
  parallel = case options[:parallel]
113
134
  when true
114
135
  1
@@ -117,6 +138,7 @@ module Switchman
117
138
  else
118
139
  options[:parallel]
119
140
  end
141
+ options.delete(:parallel)
120
142
  scope ||= Shard.order("database_server_id IS NOT NULL, database_server_id, id")
121
143
 
122
144
  if parallel > 0
@@ -125,7 +147,7 @@ module Switchman
125
147
  database_servers = scope.reorder('database_server_id').select(:database_server_id).uniq.
126
148
  map(&:database_server).compact.uniq
127
149
  scopes = Hash[database_servers.map do |server|
128
- server_scope = server.shards(scope)
150
+ server_scope = server.shards.merge(scope)
129
151
  if parallel == 1
130
152
  subscopes = [server_scope]
131
153
  else
@@ -166,13 +188,20 @@ module Switchman
166
188
  if scopes.length == 1 && subscopes.length == 1
167
189
  exception_pipe.first.close
168
190
  exception_pipe.last.close
169
- return with_each_shard(subscopes.first, categories) { yield }
191
+ return with_each_shard(subscopes.first, categories, options) { yield }
170
192
  end
171
193
  subscopes.each_with_index do |subscope, idx|
194
+ if subscopes.length > 1
195
+ name = "#{server.id} #{idx + 1}"
196
+ else
197
+ name = server.id
198
+ end
199
+
172
200
  details = Open4.pfork4(lambda do
173
201
  begin
174
202
  ::ActiveRecord::Base.clear_all_connections!
175
- with_each_shard(subscope, categories) { yield }
203
+ $0 = [$0, ARGV, name].flatten.join(' ')
204
+ with_each_shard(subscope, categories, options) { yield }
176
205
  rescue Exception => e
177
206
  exception_pipe.last.write(Marshal.dump(e))
178
207
  exception_pipe.last.flush
@@ -183,11 +212,6 @@ module Switchman
183
212
  details[1].close
184
213
  fds.concat details[2..3]
185
214
  pids << details[0]
186
- if subscopes.length > 1
187
- name = "#{server.id} #{idx + 1}"
188
- else
189
- name = server.id
190
- end
191
215
  fd_to_name_map[details[2]] = name
192
216
  fd_to_name_map[details[3]] = name
193
217
  end
@@ -239,16 +263,32 @@ module Switchman
239
263
  end
240
264
 
241
265
  result = []
266
+ exception = nil
242
267
  scope.each do |shard|
243
268
  # shard references a database server that isn't configured in this environment
244
269
  next unless shard.database_server
245
270
  close_connections_if_needed.call(shard)
246
271
  shard.activate(*categories) do
247
- result.concat Array(yield)
272
+ begin
273
+ result.concat Array(yield)
274
+ rescue
275
+ case options[:exception]
276
+ when :ignore
277
+ when :defer
278
+ exception ||= $!
279
+ when Proc
280
+ options[:exception].call
281
+ when :raise
282
+ raise
283
+ else
284
+ raise
285
+ end
286
+ end
248
287
  end
249
288
  previous_shard = shard
250
289
  end
251
290
  close_connections_if_needed.call(Shard.current)
291
+ raise exception if exception
252
292
  result
253
293
  end
254
294
 
@@ -475,11 +515,16 @@ module Switchman
475
515
  local_id + self.id * IDS_PER_SHARD
476
516
  end
477
517
 
518
+ def ==(rhs)
519
+ return true if rhs.is_a?(DefaultShard) && default?
520
+ super
521
+ end
522
+
478
523
  private
479
524
 
480
525
  def clear_cache
481
526
  Shard.default.activate do
482
- Rails.cache.delete(['shard', id].join('/'))
527
+ ::Rails.cache.delete(['shard', id].join('/'))
483
528
  end
484
529
  end
485
530
 
@@ -0,0 +1,26 @@
1
+ module Switchman
2
+ module ActionController
3
+ module Caching
4
+ module ConfigMethods
5
+ # always go through Rails.cache, which will give you the cache store
6
+ # appropriate to the current shard.
7
+ def cache_store
8
+ ::Rails.cache
9
+ end
10
+
11
+ # disallow assigning to ActionController::Base.cache_store or
12
+ # ActionController::Base#cache_store for the same reasons we disallow
13
+ # assigning to Rails.cache
14
+ def cache_store=(cache)
15
+ raise NoMethodError
16
+ end
17
+ end
18
+
19
+ include ConfigMethods
20
+
21
+ def self.included(base)
22
+ base.extend(ConfigMethods)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,3 +1,5 @@
1
+ require 'switchman/sharded_instrumenter'
2
+
1
3
  module Switchman
2
4
  module ActiveRecord
3
5
  module AbstractAdapter
@@ -6,6 +8,15 @@ module Switchman
6
8
  def shard
7
9
  @shard || Shard.default
8
10
  end
11
+
12
+ def initialize_with_shard(*args)
13
+ initialize_without_shard(*args)
14
+ @instrumenter = Switchman::ShardedInstrumenter.new(@instrumenter, self)
15
+ end
16
+
17
+ def self.included(klass)
18
+ klass.alias_method_chain :initialize, :shard
19
+ end
9
20
  end
10
21
  end
11
22
  end
@@ -2,14 +2,14 @@ module Switchman
2
2
  module ActiveRecord
3
3
  module Association
4
4
  def self.included(klass)
5
- %w{build_record load_target scoped}.each do |method|
5
+ %w{build_record creation_attributes load_target scoped}.each do |method|
6
6
  klass.alias_method_chain(method, :sharding)
7
7
  end
8
8
  end
9
9
 
10
10
  def shard
11
- # polymorphic associations assume the same shard as the owning item
12
11
  if @reflection.options[:polymorphic] || @reflection.klass.shard_category == @owner.class.shard_category
12
+ # polymorphic associations assume the same shard as the owning item
13
13
  @owner.shard
14
14
  else
15
15
  Shard.default
@@ -26,7 +26,41 @@ module Switchman
26
26
 
27
27
  def scoped_with_sharding
28
28
  shard_value = @reflection.options[:multishard] ? @owner : self.shard
29
- self.shard.activate { scoped_without_sharding.shard(shard_value, :association) }
29
+ @owner.shard.activate { scoped_without_sharding.shard(shard_value, :association) }
30
+ end
31
+
32
+ def creation_attributes_with_sharding
33
+ attributes = creation_attributes_without_sharding
34
+
35
+ # translate keys
36
+ if reflection.macro.in?([:has_one, :has_many]) && !options[:through]
37
+ attributes[reflection.foreign_key] = Shard.relative_id_for(owner[reflection.active_record_primary_key], owner.shard, self.shard)
38
+ end
39
+ attributes
40
+ end
41
+ end
42
+
43
+ module BelongsToAssociation
44
+ def self.included(klass)
45
+ klass.send(:alias_method_chain, :replace_keys, :sharding)
46
+ end
47
+
48
+ def replace_keys_with_sharding(record)
49
+ if record && record.class.sharded_column?(reflection.association_primary_key(record.class))
50
+ foreign_id = record[reflection.association_primary_key(record.class)]
51
+ owner[reflection.foreign_key] = Shard.relative_id_for(foreign_id, record.shard, owner.shard)
52
+ else
53
+ replace_keys_without_sharding(record)
54
+ end
55
+ end
56
+
57
+ def shard
58
+ if @owner.class.sharded_column?(@reflection.foreign_key) &&
59
+ foreign_id = @owner[@reflection.foreign_key]
60
+ Shard.shard_for(foreign_id, @owner.shard)
61
+ else
62
+ super
63
+ end
30
64
  end
31
65
  end
32
66
 
@@ -25,8 +25,9 @@ module Switchman
25
25
  protected
26
26
 
27
27
  def reflection_for_integer_attribute(attr_name)
28
+ attr_name = attr_name.to_s
28
29
  columns_hash[attr_name] && columns_hash[attr_name].type == :integer &&
29
- reflections.find { |_, r| r.belongs_to? && r.foreign_key == attr_name }.try(:last)
30
+ reflections.find { |_, r| r.belongs_to? && r.foreign_key.to_s == attr_name }.try(:last)
30
31
  rescue ::ActiveRecord::StatementInvalid
31
32
  # this is for when models are referenced in initializers before migrations have been run
32
33
  nil
@@ -61,7 +62,7 @@ module Switchman
61
62
  if reflection.options[:polymorphic]
62
63
  # a polymorphic association has to be discovered at runtime. This code ends up being something like
63
64
  # context_type.try(:constantize).try(:shard_category) || :default
64
- "#{reflection.foreign_type}.try(:constantize).try(:shard_category) || :default"
65
+ "read_attribute(:#{reflection.foreign_type}).try(:constantize).try(:shard_category) || :default"
65
66
  else
66
67
  # otherwise we can just return a symbol for the statically known type of the association
67
68
  reflection.klass.shard_category.inspect
@@ -2,7 +2,6 @@ module Switchman
2
2
  module ActiveRecord
3
3
  module Base
4
4
  module ClassMethods
5
- attr_writer :shard_category
6
5
  delegate :shard, :to => :scoped
7
6
 
8
7
  def shard_category
@@ -24,22 +23,43 @@ module Switchman
24
23
 
25
24
  def integral_id?
26
25
  if @integral_id == nil
27
- @integral_id = columns_hash[primary_key].type == :integer
26
+ @integral_id = columns_hash[primary_key].try(:type) == :integer
28
27
  end
29
28
  @integral_id
30
29
  end
31
30
 
32
- def transaction(*args, &block)
33
- return super if !block || !current_scope
34
- super(*args) do
35
- current_scope.activate(&block)
31
+ def transaction(*args)
32
+ if current_scope
33
+ current_scope.activate do
34
+ db = Shard.current(shard_category).database_server
35
+ if ::Shackles.environment != db.shackles_environment
36
+ db.unshackle { super }
37
+ else
38
+ super
39
+ end
40
+ end
41
+ else
42
+ db = Shard.current(shard_category).database_server
43
+ if ::Shackles.environment != db.shackles_environment
44
+ db.unshackle { super }
45
+ else
46
+ super
47
+ end
36
48
  end
37
49
  end
38
50
  end
39
51
 
40
52
  def self.included(klass)
41
53
  klass.extend(ClassMethods)
42
- klass.set_callback(:initialize, :before) { @shard = Shard.current(self.class.shard_category) }
54
+ klass.set_callback(:initialize, :before) do
55
+ unless @shard
56
+ if self.class.sharded_primary_key?
57
+ @shard = Shard.shard_for(self[self.class.primary_key], Shard.current(self.class.shard_category))
58
+ else
59
+ @shard = Shard.current(self.class.shard_category)
60
+ end
61
+ end
62
+ end
43
63
  end
44
64
 
45
65
  def shard
@@ -49,23 +69,25 @@ module Switchman
49
69
  def shard=(new_shard)
50
70
  raise ::ActiveRecord::ReadOnlyRecord if !self.new_record? || @shard_set_in_stone
51
71
  if shard != new_shard
52
- # TODO: adjust foreign keys
72
+ attributes.each do |attr, value|
73
+ self[attr] = Shard.relative_id_for(value, shard, new_shard) if self.class.sharded_column?(attr)
74
+ end
53
75
  @shard = new_shard
54
76
  end
55
77
  end
56
78
 
57
79
  def save(*args)
58
80
  @shard_set_in_stone = true
59
- shard.activate(self.class.shard_category) { super }
81
+ self.class.with_scope(self.class.shard(shard), :overwrite) { super }
60
82
  end
61
83
 
62
84
  def save!(*args)
63
85
  @shard_set_in_stone = true
64
- shard.activate(self.class.shard_category) { super }
86
+ self.class.with_scope(self.class.shard(shard), :overwrite) { super }
65
87
  end
66
88
 
67
89
  def destroy
68
- shard.activate(self.class.shard_category) { super }
90
+ self.class.with_scope(self.class.shard(shard), :overwrite) { super }
69
91
  end
70
92
 
71
93
  def clone
@@ -77,9 +99,9 @@ module Switchman
77
99
  result
78
100
  end
79
101
 
80
- def transaction(&block)
81
- shard.activate do
82
- super
102
+ def transaction(options={}, &block)
103
+ shard.activate(self.class.shard_category) do
104
+ self.class.transaction(options, &block)
83
105
  end
84
106
  end
85
107
 
@@ -88,7 +110,14 @@ module Switchman
88
110
  end
89
111
 
90
112
  def to_param
91
- Shard.short_id_for(self.id) if persisted?
113
+ short_id = Shard.short_id_for(id)
114
+ short_id && short_id.to_s
115
+ end
116
+
117
+ def initialize_dup(*args)
118
+ copy = super
119
+ @shard_set_in_stone = false
120
+ copy
92
121
  end
93
122
  end
94
123
  end
@@ -18,11 +18,10 @@ module Switchman
18
18
  end
19
19
  end
20
20
 
21
- # TODO: grouped calculations
22
21
  def execute_simple_calculation_with_sharding(operation, column_name, distinct)
23
22
  operation = operation.to_s.downcase
24
23
  if operation == "average"
25
- result = calculate_average(column_name, distinct)
24
+ result = calculate_simple_average(column_name, distinct)
26
25
  else
27
26
  result = self.activate{ |relation| relation.send(:execute_simple_calculation_without_sharding, operation, column_name, distinct) }
28
27
  if result.is_a?(Array)
@@ -39,7 +38,7 @@ module Switchman
39
38
  result
40
39
  end
41
40
 
42
- def calculate_average(column_name, distinct)
41
+ def calculate_simple_average(column_name, distinct)
43
42
  # See activerecord#execute_simple_calculation
44
43
  relation = reorder(nil)
45
44
  column = aggregate_column(column_name)
@@ -58,6 +57,149 @@ module Switchman
58
57
  end
59
58
  result
60
59
  end
60
+
61
+ # See activerecord#execute_grouped_calculation
62
+ def execute_grouped_calculation(operation, column_name, distinct)
63
+ opts = grouped_calculation_options(operation.to_s.downcase, column_name, distinct)
64
+
65
+ relation = build_grouped_calculation_relation(opts)
66
+ target_shard = Shard.current(:default)
67
+
68
+ rows = relation.activate do |rel, shard|
69
+ calculated_data = @klass.connection.select_all(rel)
70
+
71
+ if opts[:association]
72
+ key_ids = calculated_data.collect { |row| row[opts[:group_aliases].first] }
73
+ key_records = opts[:association].klass.base_class.where(:id => key_ids)
74
+ key_records = Hash[key_records.map { |r| [Shard.relative_id_for(r, shard, target_shard), r] }]
75
+ end
76
+
77
+ calculated_data.map do |row|
78
+ row[opts[:aggregate_alias]] = type_cast_calculated_value(
79
+ row[opts[:aggregate_alias]], column_for(opts[:column_name]), opts[:operation])
80
+ row['count'] = row['count'].to_i if opts[:operation] == 'average'
81
+
82
+ opts[:group_columns].each do |aliaz, column|
83
+ if opts[:associated] && (aliaz == opts[:group_aliases].first)
84
+ row[aliaz] = key_records[Shard.relative_id_for(row[aliaz], shard, target_shard)]
85
+ elsif column && @klass.sharded_column?(column.name)
86
+ row[aliaz] = Shard.relative_id_for(type_cast_calculated_value(row[aliaz], column), shard, target_shard)
87
+ end
88
+ end
89
+ row
90
+ end
91
+ end
92
+
93
+ compact_grouped_calculation_rows(rows, opts)
94
+ end
95
+
96
+ private
97
+ def grouped_calculation_options(operation, column_name, distinct)
98
+ opts = {:operation => operation, :column_name => column_name, :distinct => distinct}
99
+
100
+ opts[:aggregate_alias] = aggregate_alias_for(operation, column_name)
101
+ group_attrs = @group_values
102
+ if group_attrs.first.respond_to?(:to_sym)
103
+ association = @klass.reflect_on_association(group_attrs.first.to_sym)
104
+ associated = group_attrs.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
105
+ group_fields = Array(associated ? association.foreign_key : group_attrs)
106
+ else
107
+ group_fields = group_attrs
108
+ end
109
+
110
+ group_aliases = group_fields.map { |field| column_alias_for(field) }
111
+ group_columns = group_aliases.zip(group_fields).map { |aliaz,field|
112
+ [aliaz, column_for(field)]
113
+ }
114
+
115
+ opts.merge!(:association => association, :associated => associated,
116
+ :group_aliases => group_aliases, :group_columns => group_columns,
117
+ :group_fields => group_fields)
118
+
119
+ opts
120
+ end
121
+
122
+ def aggregate_alias_for(operation, column_name)
123
+ if operation == 'count' && column_name == :all
124
+ 'count_all'
125
+ elsif operation == 'average'
126
+ 'average'
127
+ else
128
+ column_alias_for(operation, column_name)
129
+ end
130
+ end
131
+
132
+ def build_grouped_calculation_relation(opts)
133
+ group = @klass.connection.adapter_name == 'FrontBase' ? opts[:group_aliases] : opts[:group_fields]
134
+
135
+ select_values = [
136
+ operation_over_aggregate_column(
137
+ aggregate_column(opts[:column_name]),
138
+ opts[:operation],
139
+ opts[:distinct]).as(opts[:aggregate_alias])
140
+ ]
141
+ if opts[:operation ]== 'average'
142
+ # include count in average so we can recalculate the average
143
+ # across all shards if needed
144
+ select_values << operation_over_aggregate_column(
145
+ aggregate_column(opts[:column_name]),
146
+ 'count', opts[:distinct]).as('count')
147
+ end
148
+
149
+ select_values += @select_values unless @having_values.empty?
150
+ select_values.concat opts[:group_fields].zip(opts[:group_aliases]).map { |field,aliaz|
151
+ if field.respond_to?(:as)
152
+ field.as(aliaz)
153
+ else
154
+ "#{field} AS #{aliaz}"
155
+ end
156
+ }
157
+
158
+ relation = except(:group).group(group)
159
+ relation.select_values = select_values
160
+ relation
161
+ end
162
+
163
+ def compact_grouped_calculation_rows(rows, opts)
164
+ result = ::ActiveSupport::OrderedHash.new
165
+ rows.each do |row|
166
+ key = opts[:group_columns].map { |aliaz, column| row[aliaz] }
167
+ key = key.first if key.size == 1
168
+ value = row[opts[:aggregate_alias]]
169
+
170
+ if opts[:operation] == 'average'
171
+ if result.has_key?(key)
172
+ old_value, old_count = result[key]
173
+ new_count = old_count + row['count']
174
+ new_value = ((old_value * old_count) + (value * row['count'])) / new_count
175
+ result[key] = [new_value, new_count]
176
+ else
177
+ result[key] = [value, row['count']]
178
+ end
179
+ else
180
+ if result.has_key?(key)
181
+ case opts[:operation]
182
+ when "count", "sum"
183
+ result[key] += value
184
+ when "minimum"
185
+ result[key] = value if value < result[key]
186
+ when "maximum"
187
+ result[key] = value if value > result[key]
188
+ end
189
+ else
190
+ result[key] = value
191
+ end
192
+ end
193
+ end
194
+
195
+ if opts[:operation] == 'average'
196
+ result = Hash[result.map{|k, v| [k, v.first]}]
197
+ end
198
+
199
+ result
200
+ end
201
+
202
+
61
203
  end
62
204
  end
63
205
  end
@@ -54,6 +54,17 @@ module Switchman
54
54
  @shard_connection_pools)
55
55
  connection_pools[spec] = proxy
56
56
 
57
+ if first_time
58
+ if Shard.default.database_server.config[:prefer_slave]
59
+ Shard.default.database_server.shackle!
60
+ end
61
+
62
+ if Shard.default.is_a?(DefaultShard) && Shard.default.database_server.config[:slave]
63
+ Shard.default.database_server.shackle!
64
+ Shard.default(true)
65
+ end
66
+ end
67
+
57
68
  initialize_categories(model)
58
69
  @class_to_pool[name] = proxy
59
70
 
@@ -64,12 +64,14 @@ module Switchman
64
64
  }.map { |thread| thread.object_id }
65
65
  keys.each do |key|
66
66
  conns = @reserved_connections[key]
67
- ActiveSupport::Deprecation.warn(<<-eowarn) if conn.in_use?
67
+ conns.each do |conn|
68
+ ::ActiveSupport::Deprecation.warn(<<-eowarn) if conn.in_use?
68
69
  Database connections will not be closed automatically, please close your
69
70
  database connection at the end of the thread by calling `close` on your
70
71
  connection. For example: ActiveRecord::Base.connection.close
71
- eowarn
72
- conns.each { |conn| checkin conn }
72
+ eowarn
73
+ checkin conn
74
+ end
73
75
  @reserved_connections.delete(key)
74
76
  end
75
77
  end