switchman 0.0.1 → 1.0.0

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 (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