読者です 読者をやめる 読者になる 読者になる

フリーエンジニアライフ

Ruby on Rails とか MovableType とかAWSやってるフリーランスウェブエンジニアの記録。

Active::Recordのトランザクション復習

Rails
user = User.find(2)
user.name = 'takeyuweb'
user.save!

User.transaction do         # A
  user.name = 'takeyuweb(trA)'
  user.save!
  
  User.transaction do       # B
    user.name = 'takeyuweb(trB)'
    user.save!
  end
  User.transaction do   # C
    user.name = 'takeyuweb(trC)'
    user.save!
    raise
  end
end

pp user.reload.name # => "takeyuweb"

user = User.find(2)
user.name = 'takeyuweb'
user.save!

User.transaction do         # A
  user.name = 'takeyuweb(trA)'
  user.save!
  
  User.transaction do       # B
    user.name = 'takeyuweb(trB)'
    user.save!
  end
  
  User.transaction(requires_new: true) do   # C
    user.name = 'takeyuweb(trC)'
    user.save!
    raise("突き抜けろ!")
  end
end
# RuntimeError: 突き抜けろ!

pp user.reload.name # => "takeyuweb"

--

user = User.find(2)
user.name = 'takeyuweb'
user.save!

User.transaction do         # A
  user.name = 'takeyuweb(trA)'
  user.save!
  
  User.transaction do       # B
    user.name = 'takeyuweb(trB)'
    user.save!
  end
  
  User.transaction(requires_new: true) do   # C
    user.name = 'takeyuweb(trC)'
    user.save!
    raise(ActiveRecord::Rollback)
  end
end
# CのActiveRecord::Rollbackは握りつぶされてそのまま処理を継続するので

pp user.reload.name # => "takeyuweb(trB)"

user = User.find(2)
user.name = 'takeyuweb'
user.save!

User.transaction do         # A
  User.transaction do       # B
    user.name = 'takeyuweb(trB)'
    user.save!
  end
  
  User.transaction(requires_new: true) do   # C
    user.name = 'takeyuweb(trC)'
    user.save!
    raise(ActiveRecord::Rollback)
  end
  
  user.name = 'takeyuweb(trA)'
  user.save!
end

pp user.reload.name # => "takeyuweb(trA)"

こうなる。

そのまま処理を継続してしまうとよくわかって使うこと

Shinjuku.rb #37 でSQSとかについて話してきました

AWS SQS Shinjuku.rb

shinjukurb.doorkeeper.jp

Shinjuku.rb に参加して話をしてきました。 非同期処理は管理画面とかに比べるとちょっと取っつきづらいのか人少なめでしたけど、そのぶんしっかりディスカッションできた気がします。

ruby aws sdk v2 SimpleWorkFlow(Aws::SWF::Client)メモ

swf ruby aws-sdk
swf = Aws::SWF::Client.new

失敗したExecutionを取り出す

WORKFLOW_DOMAIN = 'myworkflowdomain'

resp = swf.list_closed_workflow_executions(
         domain: WORKFLOW_DOMAIN,
         start_time_filter: {oldest_date: 24.hours.ago}, 
         close_status_filter: {status: 'TIMED_OUT'})
resp.execution_infos.each do |execution_info|
  pp execution_info
  # #<struct Aws::SWF::Types::WorkflowExecutionInfo
  #   execution=
  #    #<struct Aws::SWF::Types::WorkflowExecution
  #     workflow_id="fcac73cf-51c2-43f7-95cd-387a0bb4338b",
  #     run_id="22I3gtPVgL2GuGcBd9q1YvqH0D4aY66UbeQrJh+h7ExI8=">,
  #   workflow_type=
  #    #<struct Aws::SWF::Types::WorkflowType
  #     name="EncodeWorkflow.encode",
  #     version="1.20">,
  #   start_timestamp=2016-05-19 18:21:53 +0000,
  #   close_timestamp=2016-05-19 19:21:53 +0000,
  #   execution_status="CLOSED",
  #   close_status="TIMED_OUT",
  #   parent=nil,
  #   tag_list=[],
  #   cancel_requested=false>

  execution_event = swf.get_workflow_execution_history(
           domain: WORKFLOW_DOMAIN, 
           execution: execution_info.execution.to_h, 
           maximum_page_size: 1, 
           reverse_order: true).events.first
  pp execution_event
  # #<struct Aws::SWF::Types::HistoryEvent
  #  event_timestamp=2016-05-19 19:21:53 +0000,
  #  event_type="WorkflowExecutionTimedOut",
  #  event_id=183,
  #  workflow_execution_started_event_attributes=nil,
  #  workflow_execution_completed_event_attributes=nil,
  #  complete_workflow_execution_failed_event_attributes=nil,
  #  workflow_execution_failed_event_attributes=nil,
  #  fail_workflow_execution_failed_event_attributes=nil,
  #  workflow_execution_timed_out_event_attributes=
  #   #<struct Aws::SWF::Types::WorkflowExecutionTimedOutEventAttributes
  #    timeout_type="START_TO_CLOSE",
  #    child_policy="TERMINATE">,
  #  workflow_execution_canceled_event_attributes=nil,
  #  cancel_workflow_execution_failed_event_attributes=nil,
  #  workflow_execution_continued_as_new_event_attributes=nil,
  #  continue_as_new_workflow_execution_failed_event_attributes=nil,
  #  workflow_execution_terminated_event_attributes=nil,
  #  workflow_execution_cancel_requested_event_attributes=nil,
  #  decision_task_scheduled_event_attributes=nil,
  #  decision_task_started_event_attributes=nil,
  #  decision_task_completed_event_attributes=nil,
  #  decision_task_timed_out_event_attributes=nil,
  #  activity_task_scheduled_event_attributes=nil,
  #  activity_task_started_event_attributes=nil,
  #  activity_task_completed_event_attributes=nil,
  #  activity_task_failed_event_attributes=nil,
  #  activity_task_timed_out_event_attributes=nil,
  #  activity_task_canceled_event_attributes=nil,
  #  activity_task_cancel_requested_event_attributes=nil,
  #  workflow_execution_signaled_event_attributes=nil,
  #  marker_recorded_event_attributes=nil,
  #  record_marker_failed_event_attributes=nil,
  #  timer_started_event_attributes=nil,
  #  timer_fired_event_attributes=nil,
  #  timer_canceled_event_attributes=nil,
  #  start_child_workflow_execution_initiated_event_attributes=nil,
  #  child_workflow_execution_started_event_attributes=nil,
  #  child_workflow_execution_completed_event_attributes=nil,
  #  child_workflow_execution_failed_event_attributes=nil,
  #  child_workflow_execution_timed_out_event_attributes=nil,
  #  child_workflow_execution_canceled_event_attributes=nil,
  #  child_workflow_execution_terminated_event_attributes=nil,
  #  signal_external_workflow_execution_initiated_event_attributes=nil,
  #  external_workflow_execution_signaled_event_attributes=nil,
  #  signal_external_workflow_execution_failed_event_attributes=nil,
  #  external_workflow_execution_cancel_requested_event_attributes=nil,
  #  request_cancel_external_workflow_execution_initiated_event_attributes=nil,
  #  request_cancel_external_workflow_execution_failed_event_attributes=nil,
  #  schedule_activity_task_failed_event_attributes=nil,
  #  request_cancel_activity_task_failed_event_attributes=nil,
  #  start_timer_failed_event_attributes=nil,
  #  cancel_timer_failed_event_attributes=nil,
  #  start_child_workflow_execution_failed_event_attributes=nil,
  #  lambda_function_scheduled_event_attributes=nil,
  #  lambda_function_started_event_attributes=nil,
  #  lambda_function_completed_event_attributes=nil,
  #  lambda_function_failed_event_attributes=nil,
  #  lambda_function_timed_out_event_attributes=nil,
  #  schedule_lambda_function_failed_event_attributes=nil,
  #  start_lambda_function_failed_event_attributes=nil>
end

再実行

# 最初のイベントにワークフロー開始時の属性が入っているのでそれを使うことにした
execution_started_event_attributes = swf.get_workflow_execution_history(
           domain: WORKFLOW_DOMAIN, 
           execution: execution_info.execution.to_h, 
           maximum_page_size: 1, 
           reverse_order: false).events.first.workflow_execution_started_event_attributes

re_run_id = swf.start_workflow_execution({
  domain: WORKFLOW_DOMAIN,
  workflow_id: execution_info.execution.workflow_id,
  workflow_type: execution_info.workflow_type.to_h,
  task_list: execution_started_event_attributes.task_list,
  task_priority: execution_started_event_attributes.task_priority,
  input: execution_started_event_attributes.input,
  execution_start_to_close_timeout: execution_started_event_attributes.execution_start_to_close_timeout,
  tag_list: execution_started_event_attributes.tag_list,
  task_start_to_close_timeout: execution_started_event_attributes.task_start_to_close_timeout,
  child_policy: execution_started_event_attributes.child_policy,
  lambda_role: execution_started_event_attributes.lambda_role,
}).run_id

pp re_run_id # => "22K7jh/0kr9D4MGohM6aAESvZXkwuJg8WN3dN28silj1U="

OpsWorks (Chef11) で ERROR: cannot load such file -- /var/lib/aws/opsworks/cache.stage2/cookbooks/compat_resource/files/lib/compat_resource/gemspec

opsworks

いつも通りアプリのデプロイをしようとしたらエラー。

[2016-05-18T22:55:06+00:00] INFO: Loading cookbooks [apache2, apt, ark, build-essential, chef-sugar, chef_handler, compat_resource, curl, dependencies, deploy, gem_support, homebrew, mingw, mod_php5_apache2, mysql, newrelic, nginx, nodejs, opsworks, opsworks_agent_monit, opsworks_aws_flow_ruby, opsworks_bundler, opsworks_cleanup, opsworks_commons, opsworks_initial_setup, opsworks_java, opsworks_nodejs, opsworks_postgresql, opsworks_route53, opsworks_rubygems, opsworks_stack_state_sync, packages, passenger_apache2, php, python, rails, route53, ruby, s3_file, scm_helper, seven_zip, ssh_users, td-agent, test_suite, unicorn, windows, xml, yum, yum-epel]
 
================================================================================
Recipe Compile Error in /var/lib/aws/opsworks/cache.stage2/cookbooks/compat_resource/libraries/autoload.rb
================================================================================
 
 
LoadError
---------
cannot load such file -- /var/lib/aws/opsworks/cache.stage2/cookbooks/compat_resource/files/lib/compat_resource/gemspec
 
 
Cookbook Trace:
---------------
/var/lib/aws/opsworks/cache.stage2/cookbooks/compat_resource/libraries/autoload.rb:15:in `require_relative'
/var/lib/aws/opsworks/cache.stage2/cookbooks/compat_resource/libraries/autoload.rb:15:in `<top (required)>'
 
 
Relevant File Content:
----------------------
/var/lib/aws/opsworks/cache.stage2/cookbooks/compat_resource/libraries/autoload.rb:
 
8:    cookbook_version = $1
9:  
10:    if CompatResource::VERSION != cookbook_version
11:      raise "compat_resource gem version #{CompatResource::VERSION} was loaded as a gem before compat_resource cookbook version #{cookbook_version} was loaded. To remedy this, either update the cookbook to the gem version, update the gem to the cookbook version, or uninstall / stop loading the gem so early."
12:    end
13:  else
14:    # The gem is not already activated, so activate the cookbook.
15>>   require_relative '../files/lib/compat_resource/gemspec'
16:    CompatResource::GEMSPEC.activate
17:  end
18:  
19:  require 'compat_resource'
20:  
 
 
[2016-05-18T22:55:06+00:00] ERROR: Running exception handlers
[2016-05-18T22:55:06+00:00] ERROR: Exception handlers complete
[2016-05-18T22:55:06+00:00] FATAL: Stacktrace dumped to /var/lib/aws/opsworks/cache.stage2/chef-stacktrace.out
[2016-05-18T22:55:06+00:00] ERROR: cannot load such file -- /var/lib/aws/opsworks/cache.stage2/cookbooks/compat_resource/files/lib/compat_resource/gemspec
[2016-05-18T22:55:06+00:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)

解決までの経過まとめ

発覚

  • compat_resource という Chef12用のクックブックを使おうとしているみたい。ほかのコミュニティクックブックの依存がかわった?

Chef12に

Chef11のまましのぐには

  • 動いていたときと動かなくなったときの依存クックブックを比較(OpsWorksのログをみる)
  • Installing build-essential (4.0.0)これぽい
    • 依存クックブックの依存クックブック
    • 4.0.0からChef12以降専用になった
  • Berksfileで動いていたときのバージョン(3.2.0)に固定
cookbook 'build-essential', '3.2.0'

再デプロイ

古いバージョン固定なので、いずれ腐ってきてどうにもならなくなったときつらそうだが、当面はこれで・・・

Pencil高いけどいいですよ。

いいですよ。

trendy.nikkeibp.co.jp

絵描きでなくても使えます。

ExcelPowerPoint仕様書が送られてきて見積もり依頼ってことがあるのですが、紙に出して手書きしないと頭はいってこないので、これまでは印刷して手書きしてました。

それがiPad Pro(大きい方)買ってからは、iOS版のOfficeを立ち上げ、OneDriveに置いてる仕様書を開いて、その上に直接マーカーで引いたり、メモ書きしながら頭に入れたり実装を考えて、PCで開いたエクセルで工数を見積もる・・・という感じで活用しています。

あと、読書メモとか、設計のメモ(クラス図とかシーケンス図、フローチャートとか)をOneNote手書き、管理しています。

Officeでメモ書きす文にはPC(VAIOZ)のタブレットモードも悪くないですけど、ペンの滑りと持ちやすさでPencilに軍配が上がる感じです。あとやっぱりPCを変形させたりとかは、めんどくさい。

よってPencilいいですよ。高いけど。(大事なことなので二度言いました)

Amazon Linux 2015.09 に capybara-webkit gem をインストールしてRubyでブラウジングする

Ruby Capybara WebKit AmazonLinux headless

Installing gems

capybara-webkit gem

gem入れるまでがツラい。→ AmazonLinux 2015.09 に Qt5 WebKit をインストール

sudo ln -s /usr/lib64/qt5/bin/qmake /usr/bin/qmake 
sudo gem install capybara-webkit --no-ri --no-rdoc
Building native extensions.  This could take a while...
Successfully installed capybara-webkit-1.8.0
1 gem installed

headless gem

sudo yum install xorg-x11-server-Xvfb
sudo gem install headless --no-ri --no-rdoc
Fetching: headless-2.2.3.gem (100%)
Successfully installed headless-2.2.3
1 gem installed

RubyでHTMLやスクリーンショットを取得

require 'capybara-webkit'
require 'headless'

Capybara::Webkit.configure do |config|
  config.block_unknown_urls
  config.allow_url('*')
end

client = Capybara::Session.new(:webkit)
headless = Headless.new
headless.start
begin
  client.visit('http://www.google.com/')
  p client.status_code # => 200
  p client.html        # => "<!DOCTYPE html><html itemscope=\"\" itemtype=\"http://schema.org/WebPage\" ...
  client.save_screenshot('screenshot.png')
ensure
  headless.destroy
end

f:id:uzuki05:20160325135757p:plain