ãã®è¨äºã¯Rubyã¢ããã³ãã«ã¬ã³ãã¼2022ã®15æ¥ç®ã®è¨äºã§ãã
以ä¸ã®ãããªè©±ããéããã¾ã:
- AWS Lambda + API Gatewayã使ã£ã¦ã話
- LFAã¨ããWeb frameworkãã¬ãã¨æ¸ãã話
- LFAã§Lambdaã®æå éçºã楽ã«ãªãã®ã§ã¯ã¨ãã話
- Ruby 3.1ã§å°å
¥ããã
Kernel#load
ã®ä¾¿å©æ©è½ãè¶ ä¾¿å©ã¨ãã話 - Lambda颿°ãã¨ã«ç°å¢å¤æ°
ENV
ã®å 容ãå¤ãããã¨ãã話
æè¿AWS Lambdaã¹ã£ãããªè©±
æè¿èªåã§ãµã¼ãã¹ãä½ã£ã¦ããã§ããããµã¼ããµã¤ãã¯å ¨é¨AWS Lambda + API Gatewayã§ãã£ã¤ãã¦ã¾ãã EC2ã常æèµ·åãã¦ç®¡çããã®ããã ãã大ããè¦æ¨¡ã§ããªãããã´ãããã«ããã¤ãã©ã¤ã³ãã»ããã¢ãããã¦ã³ã³ããã¤ã¡ã¼ã¸ããã«ãããã®ãé¢åã ããã¿ãããªãã¨ãèããã¨ãã³ã¼ãããã®ã¾ã¾éãä»ããã¨ãã¨ã¯åãã¤ã¥ãã¦ãããAWS Lambda + API Gatewayã®çµã¿åããã便å©ã ãªã£ã¦ãã¨ã§ããã ãRubyã®ãã¼ã¸ã§ã³ã2.7ã ããªã®ãã¡ãã£ã¨ã¢ã¬ã§ãããã
ã¢ã¼ããã¯ãã£ã¯æå
ãM1 macãªã®ã¨ããªãã¨ãªã以åæå±ããææ
¨ããã£ã¦armã«ãã¦ããã§ãããbundleããã©ã¤ãã©ãªã«mysql2ã使ã£ãã¨ãããã¤ãã£ããã«ããããé¨åã®èªã¿è¾¼ã¿ãå®è¡æã«ã¡ãã£ã¨çè§£ä¸è½ãªã¨ã©ã¼ãèµ·ããã¦ãã¾ãåä½ãã¾ããã§ãã*1ãããã追ã£ãããããã¤ç¨ã®ç°å¢ãã¬ãã£ã¬ãã£ãã£ããã¨ããã®ãã¡ãã£ã¨é¢åã ã£ããã§ãpure Rubyãªruby-mysql
ã使ã£ãããã¦ã¾ãã(4.0ã«ä¸ããã®ã¯ããããã ãã©)使ã£ã¦ã¾ãã!!! Pure Rubyãªã©ã¤ãã©ãªã¯ãã°ããããJITã§éããªãã¨ãã£ã¨ãããã ãã©â¦â¦ã
ã§ãLambdaãªã®ã¯APIãã³ãã©ã®æ°ããªã¯ã¨ã¹ãæ°ãå°ãªãä»ã®ãã¡ã¯ãããã ãã©ããããµã¼ãã¹å½ãã£ã¦ã¢ããªã±ã¼ã·ã§ã³ãè¤éåããã大éã«ããLambdaã®ç®¡çã大å¤ã ããã³ã¹ããé«ããªããããããªã£ãã¨ãã®ãã¨ãèãã¦ãã®? ã¿ãããªè©±ã¯åºã¦ããã¨æãã¾ãã
åçã¨ãã¦ã¯ãæ¢ãã¦èãã¦ãªããã§ããè¶ åæã®ã¹ã¿ã¼ãã¢ãã*2ã§ã¯ç¾å¨ã®éçºå¹çã ããèããã®ãéåã§ãå½ãã£ããå½ãã£ãã¨ãã«èããããããã§ããä¸ç¢ºããªå ã ã®ãã¨ãèãã¦ä»ã®éçºãéå¹çã«ããçç±ã¯å ¨ãããã¾ããã
ã¨ã¯ãããããã¨ããæã®ãã¨ãèãã¦ãã¾ã
ã¾ãããã¨ã¯ããããèªåãåã®ä¼ç¤¾ã§çµé¨ããããã§ããã©ãããã£ã¦ã¨ãã«ãããã¨æã£ã¦ã¦ãããã®æã¯ãã®æã§æåªå ã®ãµã¼ãã¹éçºã®åªå 度è¨å®ããã£ã¦ãçµå±ãã®ç¬éã«ãããã¯ãã®ä¾¡å¤åä¸ã«å¯ä¸ããªã使¥ã¯ã©ããã¦ãå¾åãã«ãªããã§ããããã§ãå¾åãã«ãããã»ã©ãã±ã貯ã¾ã£ã¦ãã£ã¦ãæ¥ã ã®éçºå¹çã¯ã©ãã©ãè½ã¡ãããããã¯ãã£ã¦ãåãã¦ããµã¼ãã¹ã¯æ¢ããããªãããã¿ãããªãã¨ã¯èµ·ãããã©ã³ãã³ã°ã³ã¹ãã髿¢ã¾ããããéçºè ä½é¨ãæªåãããããå«ã ã
ã¿ãããªãã¨ãèããã¨ãæ°ããLambdaãã¨ããããã©ãã«ãããæ¹æ³ãããã¨ããããªããLambdaã¨ãã¦æ¸ããã³ã¼ãããã®ã¾ã¾æ®éã®ã¢ããªã±ã¼ã·ã§ã³ãµã¼ãã«æã£ã¦ãããªãããªããã¨ãããã¨ã妿³ãã¾ãã
LFAãä½ã£ã
ã¨ãããã¨ã§ãLambdaããã®ã¾ã¾mountãã¦Rackã¢ããªã±ã¼ã·ã§ã³ã«ãã¦ãã¾ãããã¬ã¼ã ã¯ã¼ã¯ãä½ã£ã¦ã¿ã¾ããããããããã°ãLambdaã«æ¥ã ãããã¤ãã¦ããã³ã¼ãããã£ãããã®ã¾ã¾UnicornãPumaä¸ã§åããã¾ãããã¡ããè¤æ°ã®Lambda functionã1ããã»ã¹ä¸ã§ä½µåããããã¾ãã
YAMLã®è¨å®ãã¡ã¤ã«ã¨ãã¦ãAPI Gatewayã®ãªã½ã¼ã¹è¨å®ã¿ãããªãã¤ã¨ããã¨ä½¿ã颿°ã®ãªã¹ããæ¸ãã¾ãã
# config.yaml --- resources: - path: /api resources: - path: /country methods: GET: myfunc-countries - path: /data resources: - path: /csv methods: GET: myfunc-data-csv - path: /json methods: GET: myfunc-data-json functions: - name: myfunc-countries handler: myfunc.Countries.process env: DATABASE_HOSTNAME: mydb.local DATABASE_PASSWORD: this-is-the-top-secret - name: myfunc-data-csv handler: myfunc.Data.process env: OUTPUT_DATA_TYPE: csv - name: myfunc-data-json handler: myfunc.Data.process env: OUTPUT_DATA_TYPE: json
ãã¨ã¯ãããRackç¨ã®config.ru
ãããLFAã使ã£ã¦èªã¿è¾¼ãã°ãã好ããªã¢ããªã±ã¼ã·ã§ã³ãµã¼ãã§èµ·åã§ããã¯ãã§ãã
require 'LFA' run LFA.ignition!('config.yaml')
åããLambda颿°ã®ãã¡ã¤ã«ã¯è¨å®ãã¡ã¤ã«ã¨åããã£ã¬ã¯ããªã«ç½®ãã¦ããã¾ããã¾ããã®ä»å¿ è¦ãªã©ã¤ãã©ãªã¯Gemfileã«æ¸ãã¦Bundlerçµç±ã§èµ·åããã¦ããã°æ®éã«ä½¿ããã¯ã*3ã
æ§æ³ã«Asakusa.rbæ°é±éåãå®è£ ã¯2æ¥ã§ã¬ããã¨ããæãã§ãªãã¨ãªãåãããª? ã¨ããç¶æ³ãªã®ã§ãrubygemsã«ãã¾ã ãªãªã¼ã¹ãã¦ãã¾ãããREADMEã«ãããã¾ããè¶³ããªãæ©è½ãããã¤ãããã®ã¨ãä½ãããã¹ããçç¡ãªã®ã§ããã®ã¸ããæ¯é±ã®Asakusaãªã©ã§å°ããã¤ãã£ã¦ãããªãªã¼ã¹ã®äºå®ã§ãã
å®é使ããã®ã«ãªãã®?
ãã¡ããç¾ç¶ã§ã¯é§ç®ã§ãã
ãã£ã½ãã§API Gatewayçãªã«ã¼ãã£ã³ã°ãããã ããªãå®ã¯ãã¾ã大ããæ©è½ããããªãã®ã§ãã¡ããã¨ä½ãããã°ãLambdaãªã³ã¼ããEC2ã«æã£ã¦ãã£ã¦ãªã©ã¤ãã®æéã¶ãã ãå»¶å½ãã¿ãããªç¨éã«ãååå½¹ç«ã¤ããããªãããªã¨æã£ã¦ã¾ãã大éã®Lambdaã管çãã¦ããã®ãã²ã¨ã¤ã®ãµã¼ããããã¯ã³ã³ããã®ç®¡çã«éç´ã§ããã®ã§ãããã ãã§ãå¬ããç¨éãããããã¦ããããããã¾ããã
ãã¨éçºä¸ã«æ°ä»ããã®ããããã£ã¦API Gateway + Lambdaã§ãã¹ããã¦ãã³ã¼ãã®æå éçºç¨ã«ã使ããã®ã§ã¯ã¨ãããç¾ç¶ã ã¨Lambdaã®ã³ã¼ããã¾ãæ¸ãä¸ãã¦ããã§API Gatewayã§ãªã½ã¼ã¹ã®è¨å®ãã¦ããããããã£ã¨ãªã¯ã¨ã¹ããæããã¨Lambdaã®ã³ã¼ãã®ãã£ãããã¹ãè¦ä»ãããã¿ãããªä¸çã§ããããããã¾ãæå ã§LFA + puma (+DB)ã§åä½ããã¨ããã¾ã§æ¸ãã¦ãããããå®éã®Lambda颿° + API Gatewayãªã½ã¼ã¹ãè¨å®ããããã¤ãã¨ããé åºã«ã§ããã
æ®æ®µã®éçºã«ããã¦ããã³ã¼ãæ¸ãæãã¦Lambdaã«ã¢ãããã¼ããã¦ãããã§è©¦ã*4ããããªãã¦ãæå ã§ã¡ããã¨HTTPãªã¯ã¨ã¹ãæãã¦æ³å®ã®çµæã帰ã£ã¦ãããã¨ã確èªãã¦ãããããã¤ã§ããã®ã¯ãæ®éã«ä¾¿å©ãªã®ã§ã¯ã¨ããæ°ããã¦ã¾ãã
LFAã®å®è£ ã®è©±
ããããRubyã®é¢ç½ãã¯ããã¯ã®è©±ã§ãã
Kernel#load
ã使ã£ã¦è¤æ°ã®Lambda颿°ãèªã¿è¾¼ã
è¤æ°ã®Lambda颿°ã®ã³ã¼ããã²ã¨ã¤ã®Rubyããã»ã¹ã«èªã¿è¾¼ãã¨ããã¾ãèããå¿ è¦ãããã®ã¯ãäºãã«è¡çªãã(ãããããªã)ããããã®é¢æ°ãã©ãããã°è¡çªãããã«èªã¿è¾¼ããããã¾ããLambda颿°ã®ã³ã¼ã*5ã¯ãã£ãã·ã¥ãªã©ã®ã¹ãã¼ããæã£ã¦ããå¯è½æ§ãããã®ã§ãä¾ãã°ç°ãªãLambda颿°ãåãã¯ã©ã¹/ã¢ã¸ã¥ã¼ã«/ãªãã¸ã§ã¯ãã使ç¨ãã¦ããã¨ãAWS Lambdaä¸ã§ã¯ããã»ã¹*6ãç°ãªã£ã¦ããããåé¡ãªãã¦ãã1ããã»ã¹ä¸ã§åä½ããRackã¢ããªã±ã¼ã·ã§ã³ã¨ãã¦ã¯å°ãã¾ãã
ãªã®ã§ãLambda颿°ã®ãã³ãã©func.Modname.method
ã«ããããã¡ã¤ã«åfunc
ãã颿°ãã¨ã«ç°ãªãåå空éã§èªã¿è¾¼ããªãããªãã¨ãããã¨ãã¾ãèãã¾ãããã®æ¹æ³ãããã£ã¤ããªæ¹æ³*7ããéªæªãªæ¹æ³*8ã¾ã§ããããèãã¦ããã§ãããåºæ¬ã«æ»ã£ã¦é¢é£ããããªã¡ã½ããã®ããã¥ã¡ã³ãã§ãå確èªãã¦ã¿ããã¨eval
ç³»ãKernel#require
ãKernel#load
ã¨è¦ã¦ããã¨ãè¦æ
£ããªããã®ãKernel#load
ã«çºè¦ãã¾ããã第2弿° wrap
ã
load(filename, wrap=false) â true
(snip)
If the optional wrap parameter is
true
, the loaded script will be executed under an anonymous module, protecting the calling programâs global namespace. If the optional wrap parameter is a module, the loaded script will be executed under the given module. In no circumstance will any local variables in the loaded file be propagated to the loading environment.
ããããã! ã¾ãã«æ¬²ããã£ããã®ããã!! è¶
便å©!!!!! Kernel#load
ã¯require
ã¨éã£ã¦åããã¡ã¤ã«ã§ãä½åº¦ãèªã¿ç´ãã¦ããããã¨ã¯ãã¡ããã¿ãªãããåç¥ã®ãã¨ã ã¨æãã¾ãããã¾ãããããªæ©è½ããã£ããªãã¦ããã£ãã£ã????????*9
ã¨ãããã¨ã§ä»¥ä¸ã®ãããªã³ã¼ãã§è©¦ãã¦ã¿ã*10ã¨ãå®å ¨ã«æå¾ éãã®åä½ã§ããåã£ã!!!
# func.rb module Function def self.process {statusCode: 200} end end # test.rb m1 = Module.new load('func.rb', m1) m1.const_get(:Function) #=> Function m1.const_get(:Function).process #=> {statusCode: 200} Function #=> NameError (æ³å®éã) m2 = Module.new load('func.rb', m2) m1.const_get(:Function).object_id == m2.const_get(:Function).object_id # => false # ã¤ã¾ãDataããµãã¤ãç°ãªãã¤ã³ã¹ã¿ã³ã¹ã¨ãã¦èªã¿è¾¼ã¾ãã¦ãã
ããããããªæ©è½ãã£ãã£ããã¨èª¿ã¹ã¦ã¿ããããªãã¨3.1ã®æ°æ©è½ã§ããããªãã¦ãã£ããç®ã«ãã¦ãã¯ããªã®ã«ããããªè¶ çµ¶ä¾¿å©æ©è½ã«æ°ä»ãã¦ããªãã£ããªãã¦â¦â¦ã
プロと読み解く Ruby 3.1 NEWS - クックパッド開発者ブログã«ããã¨ãDSL ã«ä½¿ãããã®ããªãï¼ãã ããã§ããããããããããããã¤ã¯ä½ã«ã§ã使ããããªæ°é
ããã¾ãããã ã£ã¦å¥ç©ºéã§load
ãã¦ä¸»åå空éã¸ã®å½±é¿ãé¿ãã¤ã¤AbstractSyntaxTree.of(anonMod.const_get(:Target).method)
ã¨ãã§ãããã§ããã便å©!*11
ããã§è¤æ°ã®Lambdaãå¥ã
ã«1ããã»ã¹ã«èªã¿è¾¼ãããã¨è¨ãããã¨ããã§ãããã¡ãã£ã¨åé¡ãããã¾ããload
ããããã¡ã¤ã«å
ã§require
ããã¦ãããã¡ã¤ã«ã®å
容ã¯ãwrap
ãã¦ããã¢ã¸ã¥ã¼ã«ä»¥ä¸ã«èªã¿è¾¼ã¾ããã¨åæã«ä¸»åå空éã«ãèªã¿è¾¼ã¾ãã¦ãã¾ãã¾ããã¾ãè¤æ°ã®Lambda颿°ã§åããã¡ã¤ã«ãrequire
(ã¾ãã¯require_relative
)ãã¦ããå ´åãrequireãããçµæã®ã¢ã¸ã¥ã¼ã«ã»ã¯ã©ã¹çã¯å
±æããã¦ãã¾ãã¾ããããã¯æ¨æ¥ã®hsbtããã®ã¨ã³ããªã§ãã©ã¤ãã©ãªãè¤æ°ãã¼ã¸ã§ã³åæã«ãã¼ãããæ©æ§ãã®ãããã§ãè¨åããã¦ã¾ãã
ãã ãããã¯ãLFAã®ç¨éã«éã£ã¦ã¯ãã¾ãåé¡ãããªãããªã¨æãã¾ããè¤æ°ã®Lambda颿°ãããã¨ãã£ã¦ããã²ã¨ã¤ã®ãã£ã¬ã¯ããªããªã¼ã«ãããªãæ®éã¯ã©ã¤ãã©ãªãèªä½ã¯ã©ã¹ãå ±æãã¦ããã¯ãã ããã§ããã¾ãããã§ããããã±ã¼ã¹ã¨ããããªãã±ã¼ã¹ãããã¾ãããå¤ãã¯åé¡ãªãã§ããã*12ã
Lambda颿°ãã¨ã«å¥ã ã®ENVãè¦ãã
AWS Lambdaã§ã¯å¤ãã®å ´åãç°å¢å¤æ°ãç¨ãã¦è¨å®ããã¨æãã¾ããããã¯ãã¡ããLambda颿°ãã¨ã«å¥ã
ã®ãã®ãæå®ã§ããªããã°ãªãããåãLambda颿°ãã³ãã©funcfile.Modname.method
ã§ãã£ã¦ããå¥é¢æ°ãªãå¥ã®ç°å¢å¤æ°ã»ãããæã£ã¦ãããã¿ãããªãã¨ãããã¨æãã¾ãããããã©ã対å¿ãããããªã¨æã£ã¦ã¾ããããKernel#load
ã®wrap
æ©è½ãè¦ããã¨ãããã以ä¸ã®ãããªå¯¾å¿ãæãä»ãã¾ããã
m = Module.new dummy_env = { "VARNAME" => "value", } m.const_set(:ENV, dummy_env) load('funcfile.rb, m)
ããã§funcfile.rb
ãloadãããã¨ãã«ã¯ãããã¬ãã«ENV
ã¨ãã¦ã使ãã¦ãããããã¼ã®ç°å¢å¤æ°ãåç
§ããã¾ãã解決ãâ¦â¦ã¨æã£ããããã¡ãããload
ããããã¡ã¤ã«å
ã§require
ããã¦ããå´ã§åç
§ããã¦ããENV
ã«ã¯å¹æãåã°ããã©ããããããªã¨æã£ããã§ããããããããªãã®ã§å¤§å
ã®ENV
ãã´ãã§ã£ã¦ãããã¨ã«ãã¾ãã
ã¾ã次ã®ããã«æ¨æºæ·»ä»ã®delegate
ã©ã¤ãã©ãªã使ã£ã¦ENV
ãç½®ãæããã¯ã©ã¹ãä½ãã¾ãã
require 'delegate' module LFA class Adapter class EnvMimic < Delegator def initialize @is_active = false @box = nil @env = ENV end def __getobj__ if @is_active @box else @env end end def __setobj__(obj) @box = obj end def mimic!(env) @box = env.dup @is_active = true yield ensure @box = nil @is_active = false end end end end
ããã¯æ®æ®µã¯ãã¹ã¦ã®ã¡ã½ããå¼ã³åºããåã«æ¬æ¥ENV
ã ã£ããã®ã«ç§»è²ãã¾ãããmimic!(env) do ... end
ã¡ã½ããã§æå®ãããããã¯å
ã§ã¯ãenv
ã§æå®ããHashã«å¯¾ãã¦ç§»è²ãã¾ããããã§ãã®ãããã¯å¼ã³åºããæããã¾ã§ã¯ãENV
ãåç
§ããã¨ããã¼ã®å
容ãè¿ã£ã¦ããã¨ããã便å©! ç°å¢å¤æ°ENV
ã®å
容ããªã¼ãã¼ã©ã¤ããããã¨ã人é¡èª°ã§ãä¸åº¦ã¯èããã¨æãã®ã§ãããã ãåãåºãã¦ã便å©ãããªãã
ã§ããã¨ã¯ããã§æ¬æ¥ã®ENV
ãç½®ãæãã¦ããã¾ããæ®éã«ããã¨è¦åãåºãã®ã§ãè¦åãæ¾ç½®ããªãè¯ãããã°ã©ãã®ãããªã¿ã¨ãã¦ç¡å¹åãã¦ããã¾ãã
# å¿ ãèªã¾ããã©ããã®ãã¡ã¤ã«ã®ãããã¬ãã«ã§ __original_warning_level = $VERBOSE begin $VERBOSE = nil ::ENV = LFA::Adapter::EnvMimic.new ensure $VERBOSE = __original_warning_level end
ããã§ã ããã解決ãããã§ãããããã§ãrequire
対象ãã¡ã¤ã«ã®ãããã¬ãã«ãã¢ã¸ã¥ã¼ã«ã»ã¯ã©ã¹å®£è¨é¨ã§ENV
ãåç
§ãã¦ããã±ã¼ã¹ã ããã«ãã¼ã§ãã¾ãããload
ãENV.mimic!
ã§ããã£ã¦ããç°ãªãLambda颿°ãåããã¡ã¤ã«ãrequire
ãã¦ããå ´åã«ã¯2åç®ã«ã¯èªã¿è¾¼ã¿ãçºçããªãããã§ããå°ã£ããªããã¨ãããã¨ã§ãããã¨ããããæ³¨æãã¦åé¿ãã¦ããã¨ãããã¨ã«ãªãã¾ãã
ã¨ãããã¨ã§
Lambdaã§æ¥½ãããã! ã¨æã£ã¦ãããä¸è¦ä¸æ¥ã®(?)OSSãã²ã¨ã¤ã§ãã¦ãã¾ããããã£ããããªãâ¦â¦ãããããã¯è¶£å³ã ãã! ä»äºã§ãã£ããããããªããã!
*1:å¤ålibmysqlclientã¾ããã®è©±ã ã¨æã
*2:ã¨ãè¨ããªãå人éçºãªã®ãç¾ç¶ã ãã©
*3:èªä¿¡ããªãæ¸ãæ¹ã«ã¡ããã¨ãããã¹ããããã¦ããªããã¨ãæãã
*4:ã¾ãstageã¨ãblue-greenã¨ããããã使ããã§ããããã©
*5:ã¢ã¸ã¥ã¼ã«ãããã¯ãã®ä»ãªãã¸ã§ã¯ã
*6:å®éã«ã¯ã³ã³ããåä½ã§ã®åé¢ãªã®ã§ãã£ã¨å¼·ãã§ãã
*7:ãã¡ã¤ã«ãæååã¨ãã¦èªã¿è¾¼ãã ä¸ã§å¤å´ãmodule Anon1; ...; end ã§ããã£ã¦evalãã
*8:TracePointã使ã£ã¦ä½ã¨ããã¦ã´ãã§ã´ãã§ããããã¯ãçãªæ¹æ³ã§ãªãããããetc
*9:å¾è¿°ãã¾ãã3.1ã®æ°æ©è½ã§ãã
*10:ã©ãã§ããããã©ãããããªãããã£ã¤ãModule.newãã¦ããªã£ã¦LFAæ¸ããªããæãã¾ããã
*11:æ£ç¢ºã«ã¯ã第2弿°ãtrueã«ãã¦ç¡å空éã«å¯¾ãã¦loadãããã¨èªä½ã¯3.0以åã§ãã§ãã¦ããããã§ããã®å ´åã§ãObjectSpaceãçµç±ãã¦loadããããã®ãåå¾ãã¦â¦â¦ã¨ãã§ããªãã¯ãªãã£ãã¿ããã ãã©
*12:ãã¨ã§READMEã«Limitationã»ã¯ã·ã§ã³ã追å ããäºå®ã§ã