Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Object-Oriented Abstraction of Redis Sorted Set

Avatar for Altech Altech
May 12, 2018

Object-Oriented Abstraction of Redis Sorted Set

Avatar for Altech

Altech

May 12, 2018
Tweet

More Decks by Altech

Other Decks in Programming

Transcript

  1. 3FEJTʹ͍ͭͯ  4FU )BTI౳ׂͱߴڃͳσʔλܕΛඋ͑ͨΠϯϝϞϦ,74  8FCαʔϏεͷߴ଎Խͱ͔ͰΑ͘࢖ΘΕΔ  4PSUFE4FU  3FEJT͕ఏڙ͢ΔσʔλܕͷҰͭ

     είΞ෇͖ͷཁૉͷू߹ʢείΞͰιʔτ͞Ε͍ͯΔʣ  ϥϯΩϯάʹ࢖͑Δ  ू߹ʹରͯ͠࿨ू߹΍ੵू߹ͱݴͬͨԋࢉ΋༻ҙ͞Ε͍ͯΔ  είΞ͸σϑΥϧτͰ଍͠߹Θ͞ΕΔ  ਪનɾݕࡧͳͲԿ͔Λ୳͢Ұཡը໘ͷ࣮૷ʹ΋࢖͑Δ
  2. 4PSUFE4FUͷ༻ྫ   4UBS਺Ͱιʔτ͞ΕͨϦϙδτϦ  ݴޠ͕3VCZ·ͨ͸1ZUIPOͰॻ͔ΕͨϦϙδτϦ  ࠓिͷτϨϯυʹࡌ͍ͬͯΔϦϙδτϦ  ࠓिͷτϨϯυʹࡌ͍ͬͯͯɺ͔ͭݴޠ͕3VCZ·ͨ͸1ZUIPOͰॻ͔ΕͨϦϙδτϦ

    
  ݴޠ͕3VCZͰॻ͔Ε͍ͯͯɺ͔ͭYNMΛΩʔϫʔυͱؚͯ͠ΉϦϙδτϦ [શͯͷϦϙδτϦ(Star ਺ΛείΞʹ࣋ͭ)] [ݴޠ͕ Ruby ͷϦϙδτϦ] ∪ [ݴޠ͕ Python ͷϦϙδτϦ] [ࠓिͷτϨϯυʹؚ·ΕΔϦϙδτϦ] [ࠓिͷτϨϯυʹؚ·ΕΔϦϙδτϦ] ∩ 
 ([ݴޠ͕ Ruby ͷϦϙδτϦ] ∪ [ݴޠ͕ Python ͷϦϙδτϦ]) [ݴޠ͕ Ruby ͷϦϙδτϦ] ∪ [จষதʹ “xml” ΛؚΉϦϙδτϦ(ݕࡧΤϯδϯͷධՁ஋ΛείΞʹ࣋ͭ)]
  3. ೋͭͷू߹ͷੵΛऔͬͯ݅औಘ͢Δ  3FEJTΫϥΠΞϯτΛ࢖͏ͱͻͨ͢Βखଓ͖తʹͳΔ  ؔ਺ʹ·ͱΊΔ͜ͱ͸Ͱ͖Δ͕ɺେ͖͘վળ͠ͳ͍ c = Redis.new if c.ttl(“src1”)

    < 0 c.zadd("src1", [[32.0, "a"], [64.0, “b”]]) c.expire("src1", 60) end if c.ttl(“src2”) < 0 c.zadd("src2", [[44.0, "a"], [21.0, "c"]]) c.expire("src2", 60) end c.zinterstore("dest", ["src1", "src2"]) c.expire("dest", 10) c.zrevrange("dest", 0, 5) #=> [“a"] dest = src1 ∩ src2
  4. جఈΫϥεɿ3FE#MPDLT4FU  SFRVJSFEkey_suffix, get  PQUJPOBMcache_time class RepositorySearchService::LanguageSet < RedBlocks::Set

    def initialize(language) raise ArgumentError unless Repository::LANGS.include?(language) @language = language end def key_suffix @language.to_s end def get Repository.where(language: @language).pluck(:id) end def cache_time 60.minutes end end
  5. جຊతͳ࢖͍ํ set = RepositorySearchService::LanduageSet.new(:Ruby) # ઌ಄5݅Λऔಘ set.ids #=> [341, 325,

    932, 421, 523] # 10ϖʔδ໨Λऔಘ set.ids(paginator: RedBlocks::Paginator.new(per: 10, page: 10)) # શମͷ݅਺Λऔಘ set.size #=> 2314 # Redis ͷΩʔΛ֬ೝ set.key #=> “RB:RepositorySearchService::LanguageSet:Ruby” # ໌ࣔతʹ Redis ্ͷσʔλΛ࠷৽ʹ͢Δ set.update!
  6. 3FE#MPDLT4FUVQEBUFσʔλͷࠩ෼ߋ৽  Ұ౓શͯΛ࡟আ͔ͯ͠Β৽͘͠σʔλΛೖΕΔͱ͍͏ํ๏ͩͱɺ σʔλ͕ͳ͘ͳͬͯ͠·͏ॠ͕ؒ͋Δ  ΞοϓσʔτલͱޙͰൺֱ͠ɺফ͑ͨཁૉͷΈΛ࡟আ͢Δ  είΞ͸શͯߋ৽͢Δ def update!

    entries = normalize_entries(validate_entries!(self.get)) removed_ids = self.ids(paginator: Paginator.all, update_if_disabled: false) - entries.map(&:last) RedBlocks.client.pipelined do RedBlocks.client.zrem(key, removed_ids) if removed_ids.size > 0 RedBlocks.client.zadd(key, all_entries) RedBlocks.client.expire(key, expiration_time) end end
  7. 3FE#MPDLT6OJPO4FU3FE#MPDLT*OUFSTFDUJPO4FU  HFU3FEJTʹ৐ͬͯͳ͍શͯͷ಺แ͢Δू߹ʹରͯ͠AVQEBUFAΛൃߦͨ͠ ޙɺA[VOJPOTUPSF EFTU TSD TSD  AͰԋࢉ݁ՌΛ֨ೲ 

    LFZ@TV⒏YΠϯελϯεϨϕϧͷࣝผࢠʹશͯͷ಺แ͢Δू߹ͷΩʔͷλϓϧ  ”[#{ruby_set.key}|#{python_set.key}]"Έ͍ͨͳΠϝʔδ set1 = RedBlocks::UnionSet.new([ruby_set, python_set]) # ܭࢉ݁ՌΛΩϟογϡ͢Δ͜ͱ΋Մೳ set1 = RedBlocks::UnionSet.new([ruby_set, python_set], cache_time: 3.minutes ) # ෳࡶͳωετ΋΋ͪΖΜՄೳ set2 = RedBlocks::IntersectionSet.new(starred_count_set, set1) ˞HFU LFZ@TV⒏Y͸ϥΠϒϥϦଆͰ࣮૷͍ͯ͠ΔͷͰɺΫϥεఆٛͳ͠Ͱར༻Մೳ
  8. 3FEJT্ͷσʔλͷੜଘظؒ  શͯͷू߹ͷϛχϚϜ ͷੜଘظؒΛΩϟογϡ ظؒͱ͸ผʹઃఆ͢Δ  ͦͷؒʹશͯͷॲཧ ͕ऴΘΔඞཁ͕͋Δ 3VCZͷू߹Λ%#͔ΒҾ͍ͯ3FEJTʹอଘ "

    1ZUIPOͷू߹Λ%#͔ΒҾ͍ͯ3FEJTʹอଘ # 3FEJT্Ͱ"ͱ#ͷ࿨ू߹Λܭࢉ͠อଘ $ ྫ͑͹ɺ͜͜ʹདྷΔ·Ͱʹ"͕FYQJSF ͪ͠ΌͬͨΒɾɾɾ $͔ΒσʔλΛҾ͘ U ಺෦ͰߦΘΕΔॲཧͷྲྀΕ 3FE#MPDLT6OJPO4FUOFX <SVCZ@TFU QZUIPO@TFU> JET
  9. 3FEJT্ͷσʔλͷੜଘظؒ  શͯͷू߹ͷϛχϚϜ ͷੜଘظؒΛΩϟογϡ ظؒͱ͸ผʹઃఆ͢Δ  ͦͷؒʹશͯͷॲཧ ͕ऴΘΔඞཁ͕͋Δ 3VCZͷू߹Λ%#͔ΒҾ͍ͯ3FEJTʹอଘ "

    1ZUIPOͷू߹Λ%#͔ΒҾ͍ͯ3FEJTʹอଘ # 3FEJT্Ͱ"ͱ#ͷ࿨ू߹Λܭࢉ͠อଘ $ ྫ͑͹ɺ͜͜ʹདྷΔ·Ͱʹ"͕FYQJSF ͪ͠ΌͬͨΒɾɾɾ $͔ΒσʔλΛҾ͘ U ಺෦ͰߦΘΕΔॲཧͷྲྀΕ 3FE#MPDLT6OJPO4FUOFX <SVCZ@TFU QZUIPO@TFU> JET def expiration_time RedBlocks.config.min_ttl + self.cache_time end
  10. 3FEJT্ͷσʔλͷੜଘظؒ  શͯͷू߹ͷϛχϚϜ ͷੜଘظؒΛΩϟογϡ ظؒͱ͸ผʹઃఆ͢Δ  ͦͷؒʹશͯͷॲཧ ͕ऴΘΔඞཁ͕͋Δ 3VCZͷू߹Λ%#͔ΒҾ͍ͯ3FEJTʹอଘ "

    1ZUIPOͷू߹Λ%#͔ΒҾ͍ͯ3FEJTʹอଘ # 3FEJT্Ͱ"ͱ#ͷ࿨ू߹Λܭࢉ͠อଘ $ ྫ͑͹ɺ͜͜ʹདྷΔ·Ͱʹ"͕FYQJSF ͪ͠ΌͬͨΒɾɾɾ $͔ΒσʔλΛҾ͘ U ಺෦ͰߦΘΕΔॲཧͷྲྀΕ 3FE#MPDLT6OJPO4FUOFX <SVCZ@TFU QZUIPO@TFU> JET def expiration_time RedBlocks.config.min_ttl + self.cache_time end
  11. 3FEJT্ͷσʔλͷੜଘظؒ  શͯͷू߹ͷϛχϚϜ ͷੜଘظؒΛΩϟογϡ ظؒͱ͸ผʹઃఆ͢Δ  ͦͷؒʹશͯͷॲཧ ͕ऴΘΔඞཁ͕͋Δ 3VCZͷू߹Λ%#͔ΒҾ͍ͯ3FEJTʹอଘ "

    1ZUIPOͷू߹Λ%#͔ΒҾ͍ͯ3FEJTʹอଘ # 3FEJT্Ͱ"ͱ#ͷ࿨ू߹Λܭࢉ͠อଘ $ ྫ͑͹ɺ͜͜ʹདྷΔ·Ͱʹ"͕FYQJSF ͪ͠ΌͬͨΒɾɾɾ $͔ΒσʔλΛҾ͘ U ಺෦ͰߦΘΕΔॲཧͷྲྀΕ 3FE#MPDLT6OJPO4FUOFX <SVCZ@TFU QZUIPO@TFU> JET def expiration_time RedBlocks.config.min_ttl + self.cache_time end RedBlocks.config.min_tll ʼ
  12. 3FE#MPDLT6OJPO4FU3FE#MPDLT*OUFSTFDUJPO4FU class RepositorySearchService def initialize(params); @params = params end def

    fetch(per: nil, page: nil) case format when :ids set.ids(paginator: RedBlocks::Paginator.new(per || 10, page || 1)) when :count set.size end end def set @set ||= build_set(@params) end def build_set(params) sets = [PublicSet.new] sets += params.map { |key, value| case key.to_sym when :languages RedBlocks::UnionSet.new(value.map { |v| LanguageSet.new(v) }) when :tags RedBlocks::UnionSet.new(value.map { |v| TagSet.new(v) }) when :keywords RedBlocks::UnionSet.new(value.map { |v| KeywordSet.new(v) }) when :order case value.to_sym when :starred StarredSet.new when :recent RecentSet.new end end }.compact RedBlocks::IntersectionSet.new(sets, cache_time: 5.minutes).unset end end
  13. ݕ౼ɿ3FE#MPDLTͰͲ͏มΘ͔ͬͨ  ಉ༷ͷϩδοΫΛ࣋ͭू߹ΛΫϥε୯ҐͰ·ͱΊΒΕΔ  ྫɿݴޠʢʹ֘౰͢ΔϦϙδτϦͷू߹ʣΫϥε3VCZʢʹ֘౰͢ΔϦϙδτ Ϧͷू߹ʣΠϯελϯε  ݸผͷσʔλΛҾ͘෦෼Λ4FUΫϥεɺ౷߹Λ4FSWJDFͰߦ ͏ͱ͍͏෼཭͕Ͱ͖Δ 

    ྫɿ֤αϒγεςϜ΁ͷΞΫηε͸4FUΫϥεͷHFUϝιουʹہॴԽ͞ΕΔ  3FEJTʹؔ͢Δ༷ʑͳৄࡉ࣮૷Λ౎౓ߟྀ͢Δඞཁ͕ͳ͍  ྫɿ࿨ू߹ͷΩʔΛߟ͑Δඞཁ͕ͳ͍ ͷΑ͏ͳ΋ͷ΋؆୯ʹͭ͘ΕΔ
  14. ·ͱΊ  3FEJTͷ4PSUFE4FUΛ3VCZͰ4FUΫϥεͱͯ͠ఏڙ  3FEJTʹରͯ͠ద੾ͳந৅ԽΛߦ͏͜ͱͰɺෳࡶͳॲཧ͕ 3VCZͰهड़Մೳʹͳͬͨ  ͦΕʹΑΓɺ3VCZͰ΋ਪનγεςϜͷ౷߹ॲཧͷΑ͏ͳ΋ͷ Λ࡞Δ͜ͱ͕Ͱ͖Δ 

    ϝΠϯͷॲཧ͸$Ͱॻ͔Εͨ3FEJTͰߦ͏͜ͱͰɺ
 3VCZͱ͍͏ݴޠͷԸܙʢߴੜ࢈ੑɾΤίγεςϜ౳ʣΛ
 ੑೳͱཱ྆͠ͳ͕ΒڗडͰ͖Δ  ࠓޙɿ4PSUFE4FUҎ֎ͷσʔλܕʹ͍ͭͯͷݕ౼ɺ౳