Skip to content

Commit 17cadb3

Browse files
tiwanariedvakf
authored andcommitted
Squash commits into one
add get_ads_ids function implement get_ads_counters and chose_best_items implement ads endpoint fix a comment add more log info add simple show_items shorten attribute names fix error log function bugs on ads/handler add more capacity info as logs implement show_items remove unnecessary params in /ads add simple conversion count upper but cannnot access with browser (just test on AWS console) implement show_item refactor a little implement delete ads function rewrite show_items with AWS.DynamoDB.DocumentClient rewrite ads with AWS.DynamoDB.DocumentClient rewrite show_item with AWS.DynamoDB.DocumentClient implement conversion resource implement update_item refactor codes implement add_place moved functions into a subfolder add decodeURIComponent to decode path params refactor a little implement delete_place fix the name of target column fix ads to return ads_contents implement add new place during adds add a test script that creates requests add README.md update README add condition to put new place update README add database access info into README update database arn in s-resources-cf.json add null check for ads_ids in /ads update the url_head of rest.rb for the dev update README.md reduce the max of expected memory size add GET method in conversion add more test lines add more console.log in conversion add condition check in update_item to prevent overwriting items remove conditional check from update_item fix ads to return JSONP fix a log function call remove console.info functions which had showed results from dynamodb because it really affected duration times update README fix README a little change the coefficient of UCB bonus from 2 to 0.5 Anonymize callback function name
1 parent 289a023 commit 17cadb3

28 files changed

Lines changed: 1296 additions & 22 deletions

README.md

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
# インターン課題
2+
ピクシブ百科事典に表示される広告の最適化
3+
4+
## 概要
5+
- ピクシブ百科事典の記事 (place) ごとに,各広告の表示回数 (impression) とコンバージョン数 (conversion) を保持して,コンバージョン率が最適になる広告を 3 つ求めるバンディット問題と考える
6+
- [Amazon API Gateway]( https://goo.gl/ha1f1a ) でリクエストを受け取り,[AWS Lambda]( http://goo.gl/avy4al ) を使って,DynamoDB に保存された情報を利用・更新して最適な広告を選んでいく
7+
- [Serverless Framework]( https://goo.gl/vMO6PI ) を使って,Amazon Gateway API から Amazon Lambda を利用している
8+
9+
10+
## エンドポイント一覧
11+
パラメータの渡し方は Tool の項目を参考に.
12+
- /ads
13+
- GET
14+
- 概要: 広告の選択
15+
- input: {place_id}
16+
- output: {3 つの広告の情報} (現在は JSONP で返却)
17+
- /conversion
18+
- GET
19+
- 概要: conversion を行ったことを記録
20+
- input: {place_id, ads_id}
21+
- POST
22+
- 概要: conversion を行ったことを記録
23+
- input: {place_id, ads_id} (JSON. body で指定する)
24+
- /admin
25+
- /ads
26+
- GET
27+
- 概要: 広告の一覧取得
28+
- output: {すべての広告の情報}
29+
- /{ads_id}
30+
- DELETE
31+
- 概要: 表示する広告の削除
32+
- input: {ads_id (pathで指定する)}
33+
- GET
34+
- 概要: 広告の情報を取得
35+
- input: {ads_id (pathで指定する)}
36+
- outpu: 広告の情報
37+
- PUT
38+
- 概要: 広告の新規登録・更新
39+
- input: {ads_id (pathで指定する), ads_info (JSON. body で指定する)}
40+
- /places (これは現状ほとんど使わない)
41+
- /{place_id}
42+
- DELETE
43+
- 概要: 記事の削除
44+
- input: {place_id (pathで指定する)}
45+
- PUT
46+
- 概要: 記事の追加 (存在する場合はなにもしない)./ads が代わりに追加してくれる
47+
- input: {place_id (pathで指定する)}
48+
49+
50+
51+
## デプロイまで
52+
このレポジトリは Serverless の 1 つのプロジェクトとして作成している.
53+
54+
### Serverless のセットアップ
55+
参考: [Serverless の基本的な使い方](http://qiita.com/susieyy/items/1c2af0ef7b88b742c37a)
56+
57+
まずは,Serverless をインストールする.
58+
59+
```
60+
npm install serverless -g
61+
```
62+
63+
AWS でユーザ作成を行い,適切なポリシーを与えたグループにそのユーザを追加するか,ユーザに適切なポリシーを付与する.
64+
ユーザ作成時には,SERVERLESS_ADMIN_AWS_ACCESS_KEY_ID, SERVERLESS_ADMIN_AWS_SECRET_ACCESS_KEY を記録しておく.
65+
66+
**(適切なポリシーを与えるところは大変でした…)**
67+
68+
参考:
69+
- [IAMユーザを作成]( http://goo.gl/2D2cGT )
70+
- [ユーザと管理グループの作成・ひも付け](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/getting-started_create-admin-group.html)
71+
- これはすべての権限を与える例なので非推奨ですが参考に
72+
73+
適当なフォルダに移動して,Serverless の project を作成する.
74+
75+
```
76+
sls project create
77+
```
78+
79+
**(sls は serverless のエイリアス)**
80+
81+
すると,以下のように対話的な項目の設定画面が現れる.
82+
83+
```
84+
_______ __
85+
| _ .-----.----.--.--.-----.----| .-----.-----.-----.
86+
| |___| -__| _| | | -__| _| | -__|__ --|__ --|
87+
|____ |_____|__| \___/|_____|__| |__|_____|_____|_____|
88+
| | | The Serverless Application Framework
89+
| | serverless.com, v0.5.5
90+
`-------'
91+
92+
Serverless: Initializing Serverless Project...
93+
Serverless: Enter a name for this project: (serverless-rkbtki) [プロジェクト名 (例: pixiv)]
94+
Serverless: Enter a new stage name for this project: (dev) [ステージ名 (例: dev)]
95+
Serverless: For the "dev" stage, do you want to use an existing Amazon Web Services profile or create a new one?
96+
Existing Profile
97+
> Create A New Profile [先ほど作成した新しいユーザを紐付けるのでこちら]
98+
Serverless: Please enter the ACCESS KEY ID for your Admin AWS IAM User: [記録した SERVERLESS_ADMIN_AWS_ACCESS_KEY_ID]
99+
Serverless: Enter the SECRET ACCESS KEY for your Admin AWS IAM User: [記録した SERVERLESS_ADMIN_AWS_SECRET_ACCESS_KEY]
100+
Serverless: Enter the name of your new profile: (pixiv_dev) [このユーザのプロファイル名 (例: pixiv)]
101+
Serverless: Creating stage "dev"...
102+
Serverless: Select a new region for your stage:
103+
us-east-1
104+
us-west-2
105+
eu-west-1
106+
eu-central-1
107+
> ap-northeast-1 [Tokyo リージョンはここ]
108+
Serverless: Creating region "ap-northeast-1" in stage "dev"...
109+
Serverless: Deploying resources to stage "dev" in region "ap-northeast-1" via Cloudformation (~3 minutes)...
110+
```
111+
112+
これで現在のフォルダに [プロジェクト名] のフォルダができる.
113+
114+
### レポジトリのクローンからデプロイまで
115+
適当なフォルダで以下を実行.
116+
```
117+
git clone git@github.com:pixiv/lambda-pudding.git
118+
```
119+
上記で作成したプロジェクトの中身をすべて,このフォルダにコピー.
120+
121+
フォルダの中にある s-resource-cf.json を編集して,IAM のポリシーを設定する.
122+
現在,以下のようになっているところを,
123+
```
124+
"Resource": "arn:aws:dynamodb:[リージョン]:[アカウントID]:table/*"
125+
```
126+
AWSアカウントのIDを基に変更して,テーブルへのアクセス権限を作る.
127+
128+
アカウントID を知るためには,例えば,AWSコンソールのサービス一覧で IAM を選択し,
129+
[IAM users sign-in link:]
130+
131+
```
132+
https://[アカウントID].signin.aws.amazon.com/console
133+
```
134+
135+
からわかる.
136+
137+
変更したら
138+
```
139+
sls resources deploy
140+
```
141+
を行ってアップデートする.
142+
143+
これで設定ができたのでデプロイする.以下のコマンドを実行.
144+
```
145+
sls dash deploy
146+
```
147+
すると,以下のように選べるので,必要なfunction&endpoint を選んでデプロイ.
148+
```
149+
Serverless: Select the assets you wish to deploy:
150+
delete_item
151+
> function - delete_item
152+
endpoint - admin/ads/{ads_id} - DELETE
153+
show_item
154+
function - show_item
155+
endpoint - admin/ads/{ads_id} - GET
156+
show_items
157+
function - show_items
158+
endpoint - admin/ads - GET
159+
update_item
160+
function - update_item
161+
endpoint - admin/ads/{ads_id} - PUT
162+
add_place
163+
function - add_place
164+
endpoint - admin/places/{place_id} - PUT
165+
delete_place
166+
function - delete_place
167+
endpoint - admin/places/{place_id} - DELETE
168+
ads
169+
function - ads
170+
endpoint - ads - GET
171+
conversion
172+
function - conversion
173+
endpoint - conversion - POST
174+
- - - - -
175+
Deploy
176+
Cancel
177+
```
178+
179+
## API を叩く
180+
- URL/ endpoint名
181+
- URLは例えば `https://************.execute-api.ap-northeast-1.amazonaws.com/dev/conversion` (conversionの場合)
182+
183+
184+
## テーブル構成
185+
186+
### テーブル一覧
187+
使うテーブルは現状 3 つ.
188+
- ads_ids
189+
- 広告の id 一覧を持つテーブル
190+
- ads_contents
191+
- 広告の内容を持つテーブル
192+
- counter
193+
- 記事ごとの広告の表示回数などを持つテーブル
194+
195+
### ads_ids
196+
広告の ID 一覧を 1 項目として持つテーブル
197+
- Partition key: dummy (Number)
198+
- 0 の 1 つだけがある
199+
- 属性
200+
- ads の ID の一覧 (StringSet)
201+
202+
### ads_contents
203+
広告の情報をもつテーブル
204+
- Partition key: ads_id (String)
205+
- 属性
206+
- 広告の情報
207+
208+
### counter
209+
記事ごとに,広告の表示・コンバージョン回数をもつテーブル
210+
- Partition key: place_id (String)
211+
- 属性
212+
- impression (Number)
213+
- ads (Map<ads_id, ads_count>
214+
- ads_count
215+
- c (Number)
216+
- i (Number)
217+
218+
219+
## Tool
220+
- tool/ 以下にテスト用の Ruby のスクリプトを入れてある
221+
222+
### rest.rb
223+
サンプルとして数行記述してあるが,[REST Client]( https://goo.gl/VXLi32 ) を使って,リクエストを作成している.
224+
225+
226+
## TODO
227+
- Item の更新などのリクエストに認証を付ける
228+
- Header に認証用の Token を入れるという話だったがまだできていない
229+
- リファクタリング
230+
- 同じような関数の共通化をしたい
231+
- Response の値を正しくする
232+
- 不要な広告の削除 (compaction)
233+
- 要らなくなった広告は現状 ads_ids テーブルから項目を消すことで対処しているが,counter からも消したほうが良い
234+
- NOTE: この処理は,counter のすべての項目から属性 (ads) を削除する必要があるため,あまり頻度を多くしたくない
235+
- NOTE: この処理をしないと,counter テーブルの項目が大きくなった時に,料金が高くなる
236+
- 1 項目について,write は 1KB 単位,read は 4KB 単位で課金額が線形に伸びる
237+
- キャッシュ
238+
- 取得した項目をキャッシュしておきたい
239+
- NOTE: DynamoDB では操作する項目の数に比例した課金体系であるので,多くの項目に触ることはできるだけ減らしたほうが良い
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use strict';
2+
3+
let AWS = require('aws-sdk');
4+
let docClient = new AWS.DynamoDB.DocumentClient({region : 'ap-northeast-1'});
5+
6+
function delete_ads(ads_id, context, cb) {
7+
let params = {
8+
TableName : 'ads_ids',
9+
Key : {
10+
'dummy' : 0
11+
},
12+
UpdateExpression : 'delete #ids :ads_id',
13+
ExpressionAttributeNames : {
14+
'#ids' : 'ids'
15+
},
16+
ExpressionAttributeValues : {
17+
':ads_id' : docClient.createSet([ads_id])
18+
},
19+
ReturnConsumedCapacity : 'TOTAL'
20+
};
21+
22+
docClient.update(params, function(err, data) {
23+
if (err) {
24+
console.error('delete_ads', err);
25+
context.done(err, 'Error (delete_ads)');
26+
}
27+
else {
28+
console.info('delete_ads', data);
29+
cb();
30+
}
31+
});
32+
}
33+
34+
module.exports.handler = function(event, context, cb) {
35+
if (event.ads_id === undefined) {
36+
context.done('ads_id is undefined', null);
37+
return ;
38+
}
39+
40+
delete_ads(decodeURIComponent(event.ads_id), context, function() {
41+
context.done(null, 'OK');
42+
});
43+
};
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{
2+
"name": "delete_item",
3+
"runtime": "nodejs4.3",
4+
"description": "Serverless Lambda function for project: pixiv",
5+
"customName": false,
6+
"customRole": false,
7+
"handler": "handler.handler",
8+
"timeout": 6,
9+
"memorySize": 128,
10+
"authorizer": {},
11+
"custom": {
12+
"excludePatterns": []
13+
},
14+
"endpoints": [
15+
{
16+
"path": "admin/ads/{ads_id}",
17+
"method": "DELETE",
18+
"type": "AWS",
19+
"authorizationType": "none",
20+
"authorizerFunction": false,
21+
"apiKeyRequired": false,
22+
"requestParameters": {},
23+
"requestTemplates": {
24+
"application/json": {
25+
"ads_id": "$input.params('ads_id')"
26+
}
27+
},
28+
"responses": {
29+
"400": {
30+
"statusCode": "400"
31+
},
32+
"default": {
33+
"statusCode": "200",
34+
"responseParameters": {},
35+
"responseModels": {
36+
"application/json;charset=UTF-8": "Empty"
37+
},
38+
"responseTemplates": {
39+
"application/json;charset=UTF-8": ""
40+
}
41+
}
42+
}
43+
}
44+
],
45+
"events": [],
46+
"environment": {
47+
"SERVERLESS_PROJECT": "${project}",
48+
"SERVERLESS_STAGE": "${stage}",
49+
"SERVERLESS_REGION": "${region}"
50+
},
51+
"vpc": {
52+
"securityGroupIds": [],
53+
"subnetIds": []
54+
}
55+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
3+
let AWS = require('aws-sdk');
4+
let docClient = new AWS.DynamoDB.DocumentClient({region : 'ap-northeast-1'});
5+
6+
function get_ads_content(ads_id, context, cb) {
7+
let params = {
8+
TableName : 'ads_contents',
9+
Key : {
10+
'ads_id': ads_id
11+
},
12+
ReturnConsumedCapacity : 'TOTAL'
13+
};
14+
15+
docClient.get(params, function(err, data) {
16+
if (err) {
17+
console.error('get_ads_content', err);
18+
context.done(err, 'Error (get_ads_content)');
19+
}
20+
else {
21+
console.info('get_ads_content', data);
22+
cb(data.Item);
23+
}
24+
});
25+
}
26+
27+
module.exports.handler = function(event, context, cb) {
28+
get_ads_content(decodeURIComponent(event.ads_id), context, function(ads_content) {
29+
context.done(null, ads_content);
30+
});
31+
};

0 commit comments

Comments
 (0)