のconnection を張るのを避けるために抽象クラスを 導入する c l a s s D B 2 B a s e < A p p l i c a t i o n R e c o r d s e l f . a b s t r a c t _ c l a s s = t r u e e s t a b l i s h _ c o n n e c t i o n : d b 2 e n d c l a s s D b 2 M o d e l A < D B 2 B a s e e n d c l a s s D b 2 M o d e l B < D B 2 B a s e e n d
connection を維持したまま切り替えたい場合はR/W それぞれ専用の Model を宣言して切り替えるなどの実装が必要 c l a s s U s e r < A p p l i c a t i o n R e c o r d e s t a b l i s h _ c o n n e c t i o n : u s e r _ r e a d o n l y e n d c l a s s U s e r : : W r i t a b l e < A p p l i c a t i o n R e c o r d e s t a b l i s h _ c o n n e c t i o n : u s e r _ w r i t a b l e e n d R/W だけで良いならswitch_point というgem を使うのが一番
いる Flickr やモンストなどで採用されている UUID を使う S e c u r e R a n d o m . u u i d オリジナルUID のルー ルを作る twitter: snowflake Instagram: timestamp, shard ID, auto_increment のmod から 64bit のID を生成
a r d 1 B a s e < A p p l i c a t i o n R e c o r d e s t a b l i s h _ c o n n e c t i o n : s h a r d 1 s e l f . a b s t r a c t _ c l a s s = t r u e e n d c l a s s S h a r d 2 B a s e < A p p l i c a t i o n R e c o r d e s t a b l i s h _ c o n n e c t i o n : s h a r d 2 s e l f . a b s t r a c t _ c l a s s = t r u e e n d c l a s s U s e r : : S h a r d 1 < S h a r d 1 B a s e e n d c l a s s U s e r : : S h a r d 2 < S h a r d 2 B a s e e n d 例えばItem がshard1, shard2 を見る場合、 コネクションは共有でき る しかし同じモデルなのに継承先が違うため実装を共有できなくなる concern を使うという手もあるがやりたくない
s e r < A p p l i c a t i o n R e c o r d s e l f . a b s t r a c t _ c l a s s = t r u e e n d c l a s s U s e r : : S h a r d 1 < U s e r e s t a b l i s h _ c o n n e c t i o n : s h a r d 1 e n d c l a s s U s e r : : S h a r d 2 < U s e r e s t a b l i s h _ c o n n e c t i o n : s h a r d 2 e n d 継承先が同じモデルになるため、 実装は共有できる しかし同じDB を見る違うモデル同士でコネクションは共有できなく なる
h a r d 1 B a s e < A p p l i c a t i o n R e c o r d e s t a b l i s h _ c o n n e c t i o n : s h a r d 1 s e l f . a b s t r a c t _ c l a s s = t r u e e n d c l a s s I t e m < A p p l i c a t i o n R e c o r d s e l f . a b s t r a c t _ c l a s s = t r u e e n d c l a s s I t e m : : S h a r d 1 < I t e m d e f s e l f . c o n n e c t i o n S h a r d 1 B a s e . c o n n e c t i o n e n d e n d Model.connection をオー バー ライドして向き先を変更することで 解決 ActiveRecord の挙動に手を入れないといけない
n _ u s e r _ 0 0 1 : < < : * d e f a u l t h o s t : d b ‐ u s e r ‐ 0 0 1 p r o d u c t i o n _ u s e r _ 0 0 2 : < < : * d e f a u l t h o s t : d b ‐ u s e r ‐ 0 0 2 M i x e d G a u g e . c o n f i g u r e d o | c o n f i g | c o n f i g . d e f i n e _ c l u s t e r ( : u s e r ) d o | c l u s t e r | c l u s t e r . d e f i n e _ s l o t _ s i z e ( 1 0 2 4 ) c l u s t e r . r e g i s t e r ( 0 . . 5 1 1 , : p r o d u c t i o n _ u s e r _ 0 0 1 ) c l u s t e r . r e g i s t e r ( 5 1 2 . . 1 0 2 3 , : p r o d u c t i o n _ u s e r _ 0 0 2 ) e n d e n d Z l i b . c r c 3 2 ( k e y ) % 1 0 2 4 の結果で振り分け先を決める 最大スロット数、 範囲内での振り分けなどは自由に決められる シャー ド数が変わると振り分け先が変わるので、 シャー ド追加時は DB 側の対応が必要
< A c t i v e R e c o r d : : B a s e i n c l u d e M i x e d G a u g e : : M o d e l u s e _ c l u s t e r : u s e r d e f _ d i s t k e y : e m a i l e n d U s e r . p u t ! ( e m a i l : ' a l i c e @ e x a m p l e . c o m ' , n a m e : ' a l i c e ' ) a l i c e = U s e r . g e t ( ' a l i c e @ e x a m p l e . c o m ' ) a l i c e . a g e = 1 a l i c e . s a v e ! U s e r . a l l _ s h a r d s . f l a t _ m a p { | m | m . f i n d _ b y ( n a m e : ' a l i c e ' ) } . c o m p a c t シャー ディングキー を指定する どのクラスター を使うか指定する p u t ! g e t ! などのメソッドを使って抽象化
e n c e r : # 採番テー ブル < < : * d e f a u l t h o s t : u s e r _ s e q u e n c e r u s e r _ 0 0 1 : < < : * d e f a u l t h o s t : u s e r ‐ d b ‐ 0 0 1 u s e r _ 0 0 2 : < < : * d e f a u l t h o s t : u s e r ‐ d b ‐ 0 0 2 A c t i v e R e c o r d : : S h a r d i n g . c o n f i g u r e d o | c o n f i g | c o n f i g . d e f i n e _ s e q u e n c e r ( : u s e r ) d o | s e q u e n c e r | s e q u e n c e r . r e g i s t e r _ c o n n e c t i o n ( : u s e r _ s e q u e n c e r ) s e q u e n c e r . r e g i s t e r _ t a b l e _ n a m e ( ' u s e r _ i d ' ) e n d c o n f i g . d e f i n e _ c l u s t e r ( : u s e r ) d o | c l u s t e r | c l u s t e r . r e g i s t e r _ c o n n e c t i o n ( : u s e r _ 0 0 1 ) c l u s t e r . r e g i s t e r _ c o n n e c t i o n ( : u s e r _ 0 0 2 ) e n d e n d
< A c t i v e R e c o r d : : B a s e i n c l u d e A c t i v e R e c o r d : : S h a r d i n g : : M o d e l u s e _ s h a r d i n g : u s e r , : m o d u l o d e f i n e _ s h a r d i n g _ k e y : i d i n c l u d e A c t i v e R e c o r d : : S h a r d i n g : : S e q u e n c e r u s e _ s e q u e n c e r : u s e r b e f o r e _ p u t d o | a t t r i b u t e s | u n l e s s a t t r i b u t e s [ : i d ] a t t r i b u t e s [ : i d ] = n e x t _ s e q u e n c e _ i d e n d e n d e n d シャー ディングキー を指定 クラスター とアルゴリズムを指定 アルゴリズムはmodulo のみ next_sequence_id で採番テー ブルからID を取得
ザが選択 ID 生成: ユー ザが選択 拡張を追加 シャー ドへのルー ティング方法をユー ザが実装可能にした octopus ライクな u s i n g syntax でシャー ドを指定してモデル をfetch 可能にした コネクション周りはModel.connection のオー バー ライド実装 に変更 クラス名を無名クラスから動的定義( U s e r : : S h a r d F o r 0 0 1 な ど ) にしてassociation 対応可能に
n _ u s e r _ 0 0 1 : < < : * d e f a u l t h o s t : d b ‐ u s e r ‐ 0 0 1 p r o d u c t i o n _ u s e r _ 0 0 2 : < < : * d e f a u l t h o s t : d b ‐ u s e r ‐ 0 0 2 A c t i v e R e c o r d : : S h a r d F o r . c o n f i g u r e d o | c o n f i g | c o n f i g . d e f i n e _ c l u s t e r ( : u s e r ) d o | c l u s t e r | c l u s t e r . r e g i s t e r ( 0 , : p r o d u c t i o n _ u s e r _ 0 0 1 ) c l u s t e r . r e g i s t e r ( 1 , : p r o d u c t i o n _ u s e r _ 0 0 2 ) e n d e n d 設定方法はほぼmixed_gauge と同じ
< A c t i v e R e c o r d : : B a s e i n c l u d e A c t i v e R e c o r d : : S h a r d F o r : : M o d e l u s e _ c l u s t e r : u s e r , : h a s h _ m o d u l o d e f _ d i s t k e y : e m a i l e n d クラスター とアルゴリズムを指定
o r d : : S h a r d F o r . c o n f i g u r e d o | c o n f i g | c o n f i g . r e g i s t e r _ c o n n e c t i o n _ r o u t e r ( : m o d u l o , S i m p l e M o d u l o R o u t e r ) # i n i t i a l i z e r などで登録 c o n f i g . r e g i s t e r _ c o n n e c t i o n _ r o u t e r ( : m a p p i n g , T a b l e M a p p i n g R o u t e r ) e n d ルー ティングをユー ザが実装して登録できる c l a s s S i m p l e M o d u l o R o u t e r < A c t i v e R e c o r d : : S h a r d F o r : : C o n n e c t i o n R o u t e r d e f r o u t e ( k e y ) k e y . t o _ i % c o n n e c t i o n _ c o u n t # s h a r d c o u n t m o d u l o e n d e n d c l a s s T a b l e M a p p i n g R o u t e r < A c t i v e R e c o r d : : S h a r d F o r : : C o n n e c t i o n R o u t e r d e f r o u t e ( k e y ) M a p p i n g T a b l e . f i n d _ b y ! ( k e y ) . s h a r d _ n u m b e r # m a p p i n g e n d e n d