初心者エンジニアのシステム構築失敗談
DESCRIPTION
TRANSCRIPT
初心者エンジニアの
システム構築失敗談
@Spring_MT
13年1月14日月曜日
Profile
13年1月14日月曜日
@Spring_MTEngineer
13年1月14日月曜日
13年1月14日月曜日
福岡RRuubbyy会議0011
13年1月14日月曜日
福岡RRuubbyy会議0011
13年1月14日月曜日
初心者エンジニアの
システム構築失敗談
@Spring_MT
13年1月14日月曜日
初心者??
13年1月14日月曜日
・前職でエンジニアに転向� その前までは一切コード書いてないんです。。。
営業とかもやってたんですよ
・エンジニア歴 22年半くらい
・ruby歴はちょうど一年
13年1月14日月曜日
なので
・gitしか使ったことない......
・utf8しかしらない........
13年1月14日月曜日
エンジニアの
ゆとり世代
13年1月14日月曜日
前の環境
13年1月14日月曜日
言語フレームワーク
perl独自フレームワーク
テストはあんまりない >>__<<
13年1月14日月曜日
DDBB関連
databaseは複数
joinは基本しない
masterとslave
autoincrement使わない
13年1月14日月曜日
新しい環境
13年1月14日月曜日
なんもない。。。。
13年1月14日月曜日
13年1月14日月曜日
00からスタート
13年1月14日月曜日
フレームワーク
13年1月14日月曜日
・テスト周�りの環境が揃ってる
・新しくjoinする人((学生含めて))の学習コストが低い
13年1月14日月曜日
Rails
13年1月14日月曜日
実際はやってみたかっただけ
13年1月14日月曜日
Railsで
アプリ作る
13年1月14日月曜日
はじめの一歩
13年1月14日月曜日
課題
13年1月14日月曜日
・複数DB対応
・master slave振り分け
・採番(sequence)
・belongs_toの扱い
13年1月14日月曜日
えっ。。。
13年1月14日月曜日
いきなり大きいこと考えすぎ
でもこの時は必要だと思ったんです。。。。
13年1月14日月曜日
大規模病 >>__<<
13年1月14日月曜日
複数DBの対応master・slave振り分け
13年1月14日月曜日
方針 ・DB + master、slave毎にModelのクラスを作る
・Controllerで明示的にDBアクセス先を指定する
13年1月14日月曜日
class TestMasterShard < ActiveRecord::Base self.abstract_class = true establish_connection "#{ENV['RAILS_ENV']}_test_master"end
class FooMaster < TestMasterShard self.table_name = 'foo' attr_accessible :name validates :content_id, {presence: true}end
production_test_master: username: test password: testdevelopment_test_master: username: test password: testtest_test_master: username: test password: test
13年1月14日月曜日
・テストではまる
・明示的に書いたほうがトラブルが少なそう
octopus
13年1月14日月曜日
# config.include RSpec::Octopus config.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 end end
config.after do @connections.each do |connection| if connection.open_transactions != 0 connection.rollback_db_transaction connection.decrement_open_transactions end end end
production_test_master: username: test password: testdevelopment_test_master: username: test password: testtest_test_master: username: test password: test
13年1月14日月曜日
View
Controller Model
UserController UserMaster
UserSlave
master
slave
FeedMaster
FeedSlave
master
slave
GroupMaster
GroupSlaveslave
FeedController
GroupController
master
13年1月14日月曜日
採番sequence
13年1月14日月曜日
アプリ内で一意のIDを発行する
方針
13年1月14日月曜日
・IDがわかれば、それに紐付くデータが一意に決まる
・重複が起きない
ex)) user_id ++ コンテンツ((feed or image or ...))のID
13年1月14日月曜日
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 end
end
production_test_master: username: test password: testdevelopment_test_master: username: test password: testtest_test_master: username: test password: test
13年1月14日月曜日
belongs_to
13年1月14日月曜日
今回は複数DBを想定したので使わないことにしました>>__<<
方針
13年1月14日月曜日
課題
・Controllerからはmaster slaveを意識せずに使いたい
13年1月14日月曜日
いきなりこの時点でスケールを考えている。。。
そもそも考慮する必要があったのか?
13年1月14日月曜日
自分で見えない敵を作ってた。。。
13年1月14日月曜日
大規模病 >>__<<
13年1月14日月曜日
第二期
13年1月14日月曜日
課題
・Controllerからはmaster slaveを意識せずに使いたい
13年1月14日月曜日
・モデルを2つに分ける
** データベースとテーブルの抽象化((masterとslave毎にクラスがある))
** master/slaveの抽象化
方針
13年1月14日月曜日
UserLogic
Logic Data
UserMaster
UserSlave
FeedMaster
FeedSlave
GroupMaster
GroupSlave
FeedLogic
View
Controller Model
UserController
FeedController
GroupController
master
slave
master
slave
slave
masterGroupLogic
13年1月14日月曜日
Model Data・DBへのアクセスを管理する層
・connectionの設定、SQLを管理のみ
13年1月14日月曜日
1
class TestMasterShard < ActiveRecord::Base self.abstract_class = true establish_connection "#{ENV['RAILS_ENV']}_test_master"end
class 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
production_test_master: username: test password: testdevelopment_test_master: username: test password: testtest_test_master: username: test password: test
13年1月14日月曜日
Model Logic
・Model Dataを用いて、master/slaveの処理をまとめる層
13年1月14日月曜日
class BaseLogic include ActiveModel::MassAssignmentSecurity include ActiveRecord::AttributeAssignment include ActiveModel::Conversion include ActiveModel::Validations extend ActiveModel::Naming extend ActiveModel::Translation
def persisted?; false; endend
class TestLogic < BaseLogic attr_accessor :foo attr_accessible :foo
def get_test_one TestSlave.get_one endend
13年1月14日月曜日
UserLogic
Logic Data
UserMaster
UserSlave
FeedMaster
FeedSlave
GroupMaster
GroupSlave
FeedLogic
View
Controller Model
UserController
FeedController
GroupController
master
slave
master
slave
slave
masterGroupLogic
13年1月14日月曜日
課題
・複数のテーブルをまたいでデータを処理する場合、どこにその処理を書くのかが不定
(Model Logic or Controller)
13年1月14日月曜日
現在
13年1月14日月曜日
課題
・複数のテーブルをまたいでデータを処理する場合、どこにその処理を書くのかが不定
(Model Logic or Controller)
13年1月14日月曜日
ControllerとModelの間に一層((Context))を追加
サービス層に近い
方針
13年1月14日月曜日
View
Controller Model
UserControllerUserMaster
UserSlave
UserLogic
Logic Data
Context
UserContext
FeedMaster
FeedSlave
FeedLogic
GroupMaster
GroupSlave
GroupLogic
FeedController
GroupController
FeedContext
GroupContext
master
slave
master
slave
slave
master
DBのデータを適切な形で受け渡すのみ
ユーザーのしたいことを実現する処理の流れ
13年1月14日月曜日
Model Data
・DBへのアクセスを管理する層
・connectionの設定、SQLを管理するだけに留める
13年1月14日月曜日
Model Logic
・Data層をコントロールする層
・一つのテーブルに一つ
・受け持つテーブルのデータの処理に関してのみ責任を負う
13年1月14日月曜日
Context・ ユーザーのしたいことを実現するための処理の流れを実装する
-->>ユーザが操作する内容ごとに実装
・Controllerと11::11対応させる
13年1月14日月曜日
Controller
・Contextで作成されたデータをViewに受け渡す
cellsを使って、uriで表現されている処理のみを実装するようにしている
13年1月14日月曜日
View
・データを描画する
・ここにロジックは書かない
13年1月14日月曜日
View
Controller Model
UserControllerUserMaster
UserSlave
UserLogic
Logic Data
Context
UserContext
FeedMaster
FeedSlave
FeedLogic
GroupMaster
GroupSlave
GroupLogic
FeedController
GroupController
FeedContext
GroupContext
master
slave
master
slave
slave
master
DBのデータを適切な形で受け渡すのみ
ユーザーのしたいことを実現する処理の流れ
13年1月14日月曜日
課題
・validationをどうする?
13年1月14日月曜日
Validation
13年1月14日月曜日
・Railsは22つのvalidation
が一緒になってる
13年1月14日月曜日
・ユーザーが入�力した値のチェック
・DBに格納する前のデータのチェック
13年1月14日月曜日
今の構成
13年1月14日月曜日
View
Controller Model
UserControllerUserMaster
UserSlave
UserLogic
Logic Data
Context
UserContext
FeedMaster
FeedSlave
FeedLogic
GroupMaster
GroupSlave
GroupLogic
FeedController
GroupController
FeedContext
GroupContext
master
slave
master
slave
slave
master
DBのデータを適切な形で受け渡すのみ
ユーザーのしたいことを実現する処理の流れ
13年1月14日月曜日
Contextが入�ったことでvalidationでエラーが起きた時のARオブジェクトの受け渡しをどうするか?
13年1月14日月曜日
Contextでは複数のARオブジェクトが格納されて、その中からvalidationエラーをまとめる。。。。
13年1月14日月曜日
正直面倒
13年1月14日月曜日
Validation・ユーザーが入�力した値のチェック
・DBに格納する前のデータのチェック
13年1月14日月曜日
分ける!
13年1月14日月曜日
DBに格納する前のデータのチェック
今まで通りARのvalidateを使う
13年1月14日月曜日
ユーザーが入�力した値のチェック
??
13年1月14日月曜日
ユーザーが入�力した値のチェック
リクエストを受けとった直後が望ましい
13年1月14日月曜日
controllerのbefore_filterでやる
13年1月14日月曜日
・ユーザーが入�力した値のチェック
==>> Controllerのbefore_filter
・DBに格納する前のデータのチェック
==>> Model
13年1月14日月曜日
before_filterで
Validation
13年1月14日月曜日
DataValidatorを書きました
13年1月14日月曜日
13年1月14日月曜日
DataValidator
ARのvalidatorとほぼ同じバリデーションロジックを実装しています
13年1月14日月曜日
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: ["can't be blank", "is invalid"]}
13年1月14日月曜日
・ユーザーの入�力値
==>> Controllerのアクション毎にDataValidatorを使う
・DBに格納する値
==>> ModelでActiveRecordを使う
13年1月14日月曜日
Controllerでvalidationした後は、ユーザーの入�力値は正しい値としてみなし、DBに入�れる値をチェックする時にエラーになった場合はシステムのエラーとみなし例外で落とす
13年1月14日月曜日
VVaalliiddaattoorr
VVaalliiddaattoorr
VVaalliiddaattoorr
View
Controller Model
UUsseerrCCoonnttrroolllleerr
UUsseerrMMaasstteerr
UUsseerrSSllaavvee
UUsseerrLLooggiicc
Logic Data
Context
UUsseerrCCoonntteexxtt
FFeeeeddMMaasstteerr
FFeeeeddSSllaavvee
FFeeeeddLLooggiicc
GGrroouuppMMaasstteerr
GGrroouuppSSllaavvee
GGrroouuppLLooggiicc
FFeeeeddCCoonnttrroolllleerr
GGrroouuppCCoonnttrroolllleerr
FFeeeeddCCoonntteexxtt
GGrroouuppCCoonntteexxtt
mmaasstteerr
ssllaavvee
mmaasstteerr
ssllaavvee
ssllaavvee
mmaasstteerr
DDBBのデータを適切な形で受け渡すのみ
ユーザーのしたいことを実現する処理の流れ
13年1月14日月曜日
・この構成で落ち着いてます
・これ以上は手を入�れない予定
13年1月14日月曜日
Railsやめちゃいなよってつっこみはなしです>>__<<
13年1月14日月曜日
gem・data_validator
・rack_session_redis_store
・redis_json_serializer
13年1月14日月曜日
ご清聴ありがとうございました!
13年1月14日月曜日
時間があれば。。。。
13年1月14日月曜日
画像・ファイルストレージ
13年1月14日月曜日
・分散ファイルシステムを自前で作る?
・色々なアプリでAPIちっくに使いたい
13年1月14日月曜日
画像・ファイルストレージ
・sinatraでappは作成
・閲覧権限等は本体で管理
・自前で分散ファイルシステム構築は難しいのでS3を使用
・Storage Proxyパターン
Nginx
varnish
sinatra app
varnish
sinatra app
App(RoR)
AWS VPC
HTTP
S313年1月14日月曜日
サーバー構成
13年1月14日月曜日
オールAWS!
13年1月14日月曜日
public
subnetservice
subnetutility
APP
DB
redis
worker
subnetstorage
subnetfluentd
deliver
worker
proxy
varnish+
sinatra
f f f
f f
f f
Watch
subnetnetwork
Gateway NATSSLの変換
13年1月14日月曜日
その他・session storeにredis
・queue処理はfluentd + resque => http://spring-‐mt.tumblr.com/post/35097726578/fluent-‐plugin-‐resque
・fluentd + GrowthForecastを使った集計と可視化
13年1月14日月曜日
13年1月14日月曜日