Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

初心者エンジニアの システム構築 失敗談

9,446 views

Published on

Published in: Technology

初心者エンジニアの システム構築 失敗談

  1. 1. 初心者エンジニア の システム構築 @Spring_MT
  2. 2. Profile
  3. 3. @Spring_MTin 10xLabInfra andApp Engineer
  4. 4. 初心者?
  5. 5. ・前職でエンジニアに転向・エンジニア歴 2年半くらい・福岡歴は10ヶ月
  6. 6. 前の環境
  7. 7. 言語 フレームワーク perl 独自フレームワークテストはあんまりない >_<
  8. 8. DB関連 databaseは複数 joinは基本しない masterとslaveautoincrement使わない
  9. 9. 新しい環境
  10. 10. なんもない。。。。
  11. 11. 0からスタート
  12. 12. フレームワーク
  13. 13. ・テスト周りの環境が ってる・新しくjoinする人(学生含めて)の学習コストが低い
  14. 14. Rails
  15. 15. 実際はやってみた
  16. 16. Rails で
  17. 17. はじめの一歩
  18. 18. 課題
  19. 19. ・複数DB対応・master slave振り分け・採番(sequence)・belongs_toの扱い
  20. 20. えっ。。。
  21. 21. いきなり大きいこと考えすぎでもこの時は必要だと思ったんです。。。。
  22. 22. 大規模病>_<
  23. 23. 複数DBの対応master・slave振り分け
  24. 24. 方針・DB + master、slave毎にmodelを作る・Controllerで明示的にDBア
  25. 25. class TestMasterShard < ActiveRecord::Base self.abstract_class = true establish_connection "#{ENV[RAILS_ENV]}_test_master"endclass FooMaster < TestMasterShard self.table_name = foo attr_accessible :name validates :content_id, {presence: true}end
  26. 26. octopus・テストではまる・明示的に書いたほうがトラブルが少なそう
  27. 27. # config.include RSpec::Octopusconfig.before do shards = ActiveRecord::Base.connection_proxy.instance_variable_get(:@shards) @connections = shards.values.map(&:connection) @connections.each do |connection| connection.increment_open_transactions connection.transaction_joinable = false connection.begin_db_transaction endendconfig.after do @connections.each do |connection| if connection.open_transactions != 0 connection.rollback_db_transaction connection.decrement_open_transactions end endend
  28. 28. View Controller Model UserController UserMaster master UserSlave slave FeedController FeedMaster master FeedSlave slave GroupControlle GroupMaster master GroupSlave slave
  29. 29. 採番
  30. 30. 方針アプリ内で一意のIDを発行する
  31. 31. ・IDがわかれば、それに紐付くデータが一意に決まる・重複が起きないex) user_id + コンテンツ
  32. 32. class Sequence < SequenceShard self.table_name = seq def self.generate(model) result = self.connection.execute( "UPDATE seq SET id=LAST_INSERT_ID(id+1)" ) id = self.connection.last_inserted_id(result) or raise RuntimeError return id endend
  33. 33. belongs_to
  34. 34. 方針今回は複数DBを想定したので使わないことにしました>_<
  35. 35. 課題・Controllerからはmasterslaveを意識せずに使いたい・ Fat Model
  36. 36. いきなりこの時点でスケールを考えている。。。そもそも考慮する必要があったのか?
  37. 37. 自分で見えない敵を作ってた。。。
  38. 38. 大規模病>_<
  39. 39. 第二期
  40. 40. 課題・Controllerからはmasterslaveを意識せずに使いたい・ Fat Model
  41. 41. 方針 slave・Controllerがmasterを意識せずにModelを呼び出せる・DBへデータを取得する部分とデータを加工する部分を分ける
  42. 42. 方針ModelをLogic層とData層に分ける
  43. 43. View Controller Model Logic Data UserController UserLogic UserMaster master UserSlave slave FeedController FeedLogic FeedMaster master FeedSlave GroupControlle slave GroupLogic GroupMaster master GroupSlave slave
  44. 44. Data層・DBへのアクセスを管理する層・connectionの設定や、SQLを管理するだけに留める
  45. 45. class TestMasterShard < ActiveRecord::Base self.abstract_class = true establish_connection "#{ENV[RAILS_ENV]}_test_master"endclass FooMaster < TestMasterShard self.table_name = foo attr_accessible :name validates :content_id, {presence: true} default_scope where(is_deleted: 0) scope :hoge, lambda { |foo| where(bar = ?, bar) }end
  46. 46. Logic層・Data層を組み合わせてデータの加工を行う
  47. 47. class BaseLogic include ActiveModel::MassAssignmentSecurity include ActiveRecord::AttributeAssignment include ActiveModel::Conversion include ActiveModel::Validations extend ActiveModel::Naming extend ActiveModel::Translation def persisted?; false; endendclass TestLogic < BaseLogic attr_accessor :foo attr_accessible :foo validates :foo, {presence: true, length: {maximum: 255}} def get_test_one endend
  48. 48. View Controller Model Logic Data UserController UserLogic UserMaster master UserSlave slave FeedController FeedLogic FeedMaster master FeedSlave GroupControlle slave GroupLogic GroupMaster master GroupSlave slave
  49. 49. 課題・複数のテーブルからデータを取得する場合、どこにその処理を書くかで迷う(Logic層?、Controller?)
  50. 50. 現在
  51. 51. 課題・複数のテーブルからデータを取得する場合、どこにその処理を書くかで迷う(Logic層?、Controller?)
  52. 52. 方針ControllerとModelの間に一層(Context)を追加
  53. 53. ユーザーのしたいこと DBのデータを適切な View を実現する処理の流れ 形で受け渡すのみController Context Model Logic DataUserController UserContext UserLogic UserMaster master UserSlaveFeedController FeedContext slave FeedLogic FeedMaster masterGroupController FeedSlave GroupContext slave GroupLogic GroupMaster master GroupSlave slave
  54. 54. Model Data層・DBへのアクセスをコントロールする層。発行するSQLはここでコントロールする
  55. 55. Model Logic層・Data層をコントロールする層・基本的に一つのテーブルに一つ
  56. 56. Context・ ユーザーのしたいことを実現するための処理の流れを実装する->ユーザが操作する内容ごとに実装
  57. 57. Controller・Contextで作成されたデータをViewに受け渡すcellsを使って、uriで表現されている処理のみを実装するようにしている
  58. 58. View・データを描画する・ここにロジックは書かない
  59. 59. ユーザーのしたいこと DBのデータを適切な View を実現する処理の流れ 形で受け渡すのみController Context Model Logic DataUserController UserContext UserLogic UserMaster master UserSlaveFeedController FeedContext slave FeedLogic FeedMaster masterGroupController FeedSlave GroupContext slave GroupLogic GroupMaster master GroupSlave slave
  60. 60. 課題・validationをどうする?
  61. 61. Validation
  62. 62. ・ユーザーが入力した値のチェック・DBに格納する前のデータのチェック
  63. 63. ・Railsは2つのvalidationが一緒になってる
  64. 64. 今の構成
  65. 65. ユーザーのしたいこと DBのデータを適切な View を実現する処理の流れ 形で受け渡すのみController Context Model Logic DataUserController UserContext UserLogic UserMaster master UserSlaveFeedController FeedContext slave FeedLogic FeedMaster masterGroupController FeedSlave GroupContext slave GroupLogic GroupMaster master GroupSlave slave
  66. 66. Contextが入ったことでvalidationでエラーが起きた時のARオブジェクトの受け渡しをどうするか?
  67. 67. Contextでは複数のARオブジェクトが格納されて、その中からvalidationエラーをまとめる。。。。
  68. 68. 正直面倒
  69. 69. Validation・ユーザーが入力した値のチェック・DBに格納する前のデータのチェック
  70. 70. 分ける!
  71. 71. つまり
  72. 72. Controllerでもvalidationする
  73. 73. えっ(∩゚д゚)
  74. 74. Controllerでもvalidationする
  75. 75. Controller でもvalidation
  76. 76. 言っちゃった..
  77. 77. ((((;゚
  78. 78. ・ユーザーが入力した値のチェック => Controller・DBに格納する前のデータのチェック => Model
  79. 79. Controller でもValidationするために
  80. 80. DataValidatorってのを書きました
  81. 81. DataValidatorARのvalidatorとほぼ同じバリデーションロジックを実装しています
  82. 82. params = {foo: foo, bar: bar}validator = DataValidator::Validator.new( params, {foo: {length: {is: 4}}}, {bar: {presence: true, format: {with: /A[a-zA-Z]+z/}}})unless validator.valid? @errors = validator.errorsend@errors=> { foo: ["is the wrong length (should be 4 characters)"], bar: ["cant be blank", "is invalid"]}
  83. 83. ・ユーザーの入力値=> Controllerのアクション毎にDataValidatorを使う・DBに格納する値=> Modelで
  84. 84. Controllerでvalidationした後は、ユーザーの入力値は正しい値としてみなし、DBに入れる値をチェックする時にエラーになった場合はシス
  85. 85. ユーザーのしたいこと DBのデータを適切な View を実現する処理の流れ 形で受け渡すのみController Context Model Logic Data UserController UserContextValidator UserLogic UserMaster master UserSlave FeedController FeedContext slaveValidator FeedLogic FeedMaster masterGroupController FeedSlave GroupContext slaveValidator GroupLogic GroupMaster master GroupSlave slave
  86. 86. ・この構成で落ち着いてます・これ以上は手を入れない予定
  87. 87. Railsやめちゃ いなよってつっこみはな
  88. 88. gem・data_validator・rack_session_redis_store・redis_json_serializer
  89. 89. 画像・ファイルストレージ
  90. 90. ・分散ファイルシステムを自前で作る?・色々なアプリでAPIちっくに使いたい
  91. 91. 画像・ファイル ・sinatraでappは作AWS VPC App(RoR) 成 HTTP ・閲覧権限等は本体で Nginx 管理 varnish varnish ・自前で分散ファイル sinatra sinatra app app システム構築は難しい のでS3を使用 S3
  92. 92. サーバー構成
  93. 93. オールAWS!
  94. 94. public subnet net work Gateway NAT SSLの変換 subnet subnet subnet subnet fluentd utility storage ser vice deliver proxy APPWatch redis f f f worker varnish DB + sinatra f f worker f f
  95. 95. その他・session storeにredis・queue処理はfluentd +resque=> http://spring-mt.tumblr.com/post/35097726578/fluent-plugin-resque・fluentd + GrowthForecastを
  96. 96. 時間があれば。。。。
  97. 97. なにかあれば懇親会で!
  98. 98. ご清聴ありがとうござ

×