Media-JAWS にて登壇しました #jawsug #mediajaws

本記事はTVer Advent Calendar 2023の15日目の記事です。

はじめに

こんにちは。去年も15日目の記事を書いていたバックエンドエンジニアの伊藤です。

11/15にInterBEEに合わせて海浜幕張で開催されたMedia-JAWSにて初の登壇をしてきました。

ということで今年は登壇ブログを書いていきたいと思います。

media-jaws.doorkeeper.jp

Media-JAWSとは

以下、公式サイトからの引用です。

Media-JAWSは、例えば急激なトラフィック処理や映像や画像のワークロード処理、セキュリティ対策など、放送・ラジオ・新聞・雑誌・Web・SNSなどのメディア特有の性質が求められるサービスを、AWSを活用してどのように構築・運用しているか、といった情報交換や、交流の場として活用される勉強会です。

開催頻度は、3ヶ月に一度を目標にしています。 関東以外の方のために、基本的にライブ配信を実施します。 いずれは関東以外での開催や、関東以外の方によるリモートでの登壇なども行いたいと考えています。

ハッシュタグは #mediajaws

登壇の経緯

SREチームの加我さんから登壇しない?とお話があり、やりますということで登壇させていただきました。

やりますと言って締切が決まれば大体なんとかなるの精神です。

元々登壇はいつかしたいと思っていたのですが、今年の4月に行われたAWS Summit Tokyo 2023で色々な方々と交流する機会がありそこでモチベーションを得たことも大きなきっかけの一つです。とても良い体験でした。

登壇内容について

資料はこちらです。 speakerdeck.com 発表内容についてですが、2022/4のリニューアルを機に大幅に下がってしまったSEOのスコアを取り戻すためにバックエンドエンジニアがやったこととその延長線上で行ったOGP対応についてのお話ししました。

4行にまとめれば、以下のようなことが書いてあります。

  • 2022/04にリニューアルしたけどSEOのスコア下がっちゃった
  • 検索結果にTVer出てこないからバックエンドで対策して欲しい
  • 頑張って対策したらリニューアル前より良くなったよ
  • ついでにOGP対応もやったよ

大枠の実装は今も変わらずTVerで使われているものです。ただ実はまだ何個か対応しなければいけないこともあり現在も改修は続けられています。

特にSEO対策に使っている簡易クローラーはECSタスクで定期的に実行しているのですが、chromedpがハングしてタスクが終わらないという事象*1に立ち向かっていたり、OGP対応ではLPから切り替えきれていないページの切り替えを引き続き対応しています。アラートに一区切りつけて無事落ち着いて年を越せると良いなと思っております。

余談ですが、去年のアドベントカレンダー で寄稿したHTMLの編集の話はこのSEO対策のためにやっていたことだったので、ちょうど一年越しに取り組みについて深掘りする形の資料を書くことになりました。

また上の資料にあるTVerについての情報は以下の資料の引用になります。 TVerについて全ての資料を確認したい場合は是非こちらをご覧ください。

speakerdeck.com

おわりに

初めての登壇ということで緊張しましたが、なんとか無事終えることができました。資料作成や人前で話すこと自体卒研ぶりで、ほぼ未知の世界でしたが良い経験でした。 今年できたら良いなーくらいのゆるい目標でしたが、年内に登壇することができ満足しています。

資料のレビューをしてくれた社内の方々や、当日サポートしてくれたスタッフの方々ありがとうございました。 また機会があればどこかで登壇したいなと思いました。

SREチームの加我さん *2 によるInterBEEやMedia-JAWSの参加レポートもありますので是非。 techblog.tver.co.jp

*1:無限にアラート飛んできてとてもつらい

*2:最初から最後までとてもお世話になった

私とAWSと2023年

こんにちは。
アドテク領域のエンジニアをしています安部です。
こちらは TVer Advent Calendar 2023 の13日目の記事です。

個人的に今年はAWSに縁があった年でしたのでAWSにからめて1年を振り返ります。

1月〜3月

フロントエンド開発をがんばっていました。(いきなりAWSじゃない!)
OCJP Silver取ったりしてました。(バックエンド!)
開発業務専門でAWSをどう使っているか、どうデプロイされているのかはあまり気にしていませんでした。
転機になったのは3月にログのアラートをslackに連携したいという話が出てきて、CloudWatch LogsとLambdaを使用してslack通知をするツールを作成したことでした。
AWSのサービス駆使して色々できそう!これはサービス知っておいたほうが得だな!と思い始めた時期です。

4月〜6月

AWS Summit Tokyo参加

AWS Summit Tokyoに行きました。

個人的テーマは「AWSを知ること」でした。
その時の記事はこちら

techblog.tver.co.jp

ここでAWSのサービスを色々知りました。
認定資格を取ってみようかなとも考え始めた時期です。
余談ですが、このときにもらったクッションは今でも使ってます。

ツール作成

4月には広告入稿システムのリプレイスがありました。
このシステムと対向システムでデータの同期しているのですが、データに差分が出ていないか確認する必要があります。
そのため対向システムから不整合が発生している可能性のあるデータを連携していただき、確認するという定常業務がありました。
元々の確認方法は連携データを1データずつSQLを実行して確認する方式だったのですが、Lambdaを使い自動化しました。
ツールについては詳しい内容を別ブログにしたいなと思っています。

7月〜12月

資格取得

7月にAWS Certified Cloud Practitioner を取得しました。

なんとか合格

 

2週間Udemyの問題集をひたすら解いて挑みました。
次はASSOCIATE資格を狙っています。

CI/CD構築

Bitbucket Pipelinesを使用したCI/CD構築を担当しました。
そこでAWSのどのサービスをどのように使っているのか、どのような設定をすればいいのかがちょっとわかりました。
CLF資格取得と被っている時期にやっていたので「ここ見たぞ!」「これかやったわ!」と心のなかで呟きながら作業したり資格勉強したりしていました。
資格のための勉強が業務に繋がったので資格を取ったかいがありました。

 

JAWS Festa 参加

10月は福岡で行われたJAWS Festaに参加しました。

その時のブログはこちら

techblog.tver.co.jp福岡ならではの事例を聞くことができたり、懇親会で多くの参加者の方とお話できたりして楽しかったです。
個人的目標の「地元福岡であるテック系イベントに参加する」が達成できて嬉しくもありました。

天神のエンジニアカフェ、年末に行ってみたいなと思っています。

 

2023年まとめ

AWSに興味を持って、学び、業務に反映できた

 AWSを知ると面白そうだぞと興味を持ち、イベントに行ったり資格の取得を通して知見を広め、それを業務に還元することができました。

 おそらく年初に興味を持って学んでいなかったらツールを開発しようとも思わなかったし、CI/CDの構築も時間がかかったのではないかと思います。

 興味と業務をリンクさせることができたのは収穫でした。

・テックブログの執筆は難しい

 前職も含めイベントには何度か参加しましたが、フィードバックは社内クローズのちょっとした報告書を作成する程度でした。

 テックブログでイベントのレポートを書くということは新鮮で、どう書けばちゃんと伝わるか、何を学んだか理解してもらえるかを考え書くことに苦労しました。

 もっと文章力を上げたいと思う1年でした。

・イベント参加はおもしろい+難しい

 イベントで事例を聞くこと、ハンズオンセミナーを受講して実際に体験すること、どのように業務に活かすかを考えることはとても面白いことだと感じています。

 その一方、イベント参加者とたくさんお話したいと思いつつも「何を喋ろう…」や「今話しかけていいタイミングなのかしら…」と若干の人見知りを発動してなかなか交流を図ることができないことがありました。

 コミュニケーションの取り方難しいと改めて実感する1年でした。

来年の抱負

せっかくなので来年の抱負も書きます。
・テックブログ書く
 私が書いているブログ、今のところイベントに参加しました!系ブログしかありません。
 来年は「こんなことやったよ!」というブログも書きたいと思います。
・Lambdaともっと仲良くなる
 ツールを作ることがきっかけで使うようになったLambdaですが、まだLambdaのことを半分も活用できていない気がしています。
 もっとLamdbaを知って業務に取り入れていければと思っています。

・資格取得

 ASSOCIATE資格のどれか1つは取りたいです。

 弊社では資格取得支援制度があり、事前に申請し合格した場合は受験料の補助があります。

 すでにSAAの事前申請はしているので来年の早い時期に取得できるように勉強しています!
・イベント参加
 来年はもっといろんなイベントに行きたいと思います。
 東京・福岡はもちろん、日本全国イベント巡り(と温泉行って観光)したい!
 

New Relicをフルに活用するためにデータ量とコストに気を配る

こんにちは、TVerの加我です。
こちらは TVer Advent Calendar 2023New Relic 使ってみた情報をシェアしよう! by New Relic Advent Calendar 2023 の8日目の記事です。

みなさまNew Relicを活用していますか?サービスの信頼性を担保していますか?オブザーバビリティの導入・実現に向けてNew Relicを使い倒していますか?

New Relicは非常に高機能なオブザーバビリティプラットフォームです。TVerではフロントエンドからバックエンドまでNew Relicを活用した横断的な観測を行っています。しかしNew Relicを導入し活用していくにつれて気になってくるのがコストです。

ということで、New Relicを活用しつつコストを最適化するためのポイントについて考えていきます。

New Relicのコスト計算についておさらい

まずはNew Relicの価格設定について理解していきましょう。
New Relicでは「有償のユーザー数」と「取り込むデータ量」の2点が請求対象となります。

ユーザー数による請求については後述する有償のユーザー追加がなければ変動がない部分なのでほぼ固定費、取り込むデータ量についてはアクセス数やイベント数により大きく変動することがあるため変動費と考えることができます。つまり取り込むデータ量を最適化することが重要です。

docs.newrelic.com

こちらについてはAdministrationのPlan & usageにて利用状況を確認することができます。

Plan & usage

Plan & usageのUsage breakdownからView detailsを選択することでデータソース毎の取り込むデータ量を確認することができます。

Data ingested (GB) per day by data source

1. 有償のユーザー数

New Relicには「Full Platform User」「Core User」「Basic User」という3つのユーザー種別があります。このうち有償のユーザーとして請求対象になるのは「Full Platform User」と「Core User」です。TVerではFull Platform UserとBasic Userを利用した権限管理を行っています。Core Userはユースケースがマッチしなかったので利用していません。

ユーザー区分 利用者 利用目的
Full Platform User エンジニア
カスタマーサポート
バックエンドAPIのTransaction調査
モバイルアプリのクラッシュ調査
クラウドインフラのリソース調査
Core User - -
Basic User ディレクター
エンジニア
ダッシュボードを通じたデータ確認

ユーザー毎の権限については下記ドキュメントを参照ください。

docs.newrelic.com

2. 取り込むデータ量

イベントやメトリクスやログといったテレメトリーデータをNew Relic (NRDB) に取り込む際に請求対象となります。TVerではフロントエンドからバックエンドまでNew Relicを利用しているため、各所で発生したテレメトリーデータがNew Relic (NRDB) に取り込まれ請求対象となります。

主なデータの発生元 New Relicへの取り込み方法 主なデータソース (NRDB)
Webブラウザ New Relic Browser Browser events
Mobileアプリ New Relic Mobile Mobile events
バックエンドAPI New Relic APM APM events
クラウドインフラ
(AWSGoogle Cloud)
New Relic Infrastructure Metrics
Infrastructure integrations
各種ログ New Relic Logs Logging

docs.newrelic.com

今回はこちらの「取り込むデータ量」についてのお話です。

何が起こったのか

2022年4月のサービスリニューアル以降、定期的なNew Relic Agentのアップデートを行えていませんでした。New Relic Agentには定期的なバグフィックスやセキュリティアップデートや機能追加が行われているため、2023年3月12日頃に諸々のAgentのアップデートを行いました。しかしアップデートを実施した結果、New Relic Browser Agent (Web) の更新によりBrowser eventsで取り込むデータ量が10倍程度に膨れ上がってしまいました。

Browser eventsのデータ量が急増

New Relicのサポートにもアップデート内容の確認や原因の調査についてご相談したのですが、残念ながらこれという決定的な証拠を見つけることができませんでした。可能性としては「当初の設定の不備により必要なデータを取得できていなかった」もしくは「分散トレーシング周りのアップデートがあり取得できるイベントが増えた」かと考えています。

フロントエンド系のデータを扱うNew Relicアカウントで急激なデータ増加

BrowserEventsBytesで急激なデータ増加

当時はNew Relicを駆使してサービスの信頼性を可視化・担保するというミッションを推進していたため「今はどんどん可視化を進めていきたい」「何はともあれオブザーバビリティ」といった流れで進めていった結果、データ量やコストの意識が二の次になってしまいました。New Relicにかかるコストのオブザーバビリティは後回しになってしまい恥ずかしい限りです。

特定のURLが高頻度で閲覧されていたりスクレイピングされている可能性も疑いましたが、全体的なイベント数が増加していることを確認して改善案を考えました。

改善

取り込むデータ量の可視化と通知、そしてデータ量の削減という二軸で対応しました。

1. 取り込むデータ量の可視化 & 定期的な通知

まずは「現状どのような取り込みデータ量になっているのか」「いつからデータ量が増加したのか」「どのデータソースで増加があったのか」を判断できるようなデータおよびダッシュボードを整備しました。そのためにNew Relicアカウント別のグラフとデータソース別のグラフを用意しました。

NRQLだとこんな感じです。24時間のグラフだと増減がわかりにくかったので3日間のデータを毎日見るようにしています。

# [前日比] アカウント別のデータ量
SELECT rate(sum(GigabytesIngested), 1 day) AS avgGbIngestTimeseriesByAccount FROM NrConsumption WHERE productLine = 'DataPlatform' FACET consumingAccountName TIMESERIES AUTO SINCE 3 days AGO COMPARE WITH 1 day ago
# [前日比] データソース別のデータ量
SELECT rate(sum(GigabytesIngested), 1 day) AS avgGbIngestTimeseries FROM NrConsumption WHERE productLine = 'DataPlatform' FACET usageMetric LIMIT MAX TIMESERIES AUTO SINCE 3 days AGO COMPARE WITH 1 day ago

上記のNRQLをグラフにしたのが下記のスクリーンショットです。

New Relic全体のデータ量推移 (アカウント別とデータソース別)

また、バックエンド (New Relic APM, New Relic Infrastructure) に比べるとフロントエンド (New Relic Browser, New Relic Mobile) の方がデータ量にばらつきが大きいことがわかっているため、主要なイベント数の推移を把握できるグラフを用意しました。こちらも24時間のグラフだと増減がわかりにくかったので3日間のデータを毎日見るようにしています。

# とあるBrowserアプリケーションのグラフをイベントごとに作成
SELECT count(*) FROM AjaxRequest WHERE appName = '<ブラウザアプリ>' TIMESERIES SINCE 3 days ago
SELECT count(*) FROM BrowserInteraction WHERE appName = '<ブラウザアプリ>' TIMESERIES SINCE 3 days ago
SELECT count(*) FROM JavaScriptError WHERE appName = '<ブラウザアプリ>' TIMESERIES SINCE 3 days ago
SELECT count(*) FROM PageView WHERE appName = '<ブラウザアプリ>' TIMESERIES SINCE 3 days ago
SELECT count(*) FROM PageViewTiming WHERE appName = '<ブラウザアプリ>' TIMESERIES SINCE 3 days ago
# とあるMobileアプリケーションのグラフをイベントごとに作成
SELECT count(*) FROM Mobile WHERE appName = '<モバイルアプリ>' TIMESERIES SINCE 3 days ago
SELECT count(*) FROM MobileCrash WHERE appName = '<モバイルアプリ>' TIMESERIES SINCE 3 days ago
SELECT count(*) FROM MobileRequest WHERE appName = '<モバイルアプリ>' TIMESERIES SINCE 3 days ago
SELECT count(*) FROM MobileRequestError WHERE appName = '<モバイルアプリ>' TIMESERIES SINCE 3 days ago
SELECT count(*) FROM MobileSession WHERE appName = '<モバイルアプリ>' TIMESERIES SINCE 3 days ago
SELECT count(*) FROM MobileVideo WHERE appName = '<モバイルアプリ>' TIMESERIES SINCE 3 days ago
SELECT count(*) FROM Span WHERE appName = '<モバイルアプリ>' TIMESERIES

上記のNRQLをグラフにしたのが下記のスクリーンショットです。各グラフをStacked Bar形式にして積み上げて確認しています。

Browser系のイベント数推移

Mobile系のイベント数推移

推移を見るグラフが整備できたのであればあとは通知です。クラスメソッド株式会社の新井成一さんがNew RelicのダッシュボードをSlackへ定期的に投稿するためのツールを公開してくれていたため、こちらを利用させていただきました。

github.com

dev.classmethod.jp

実際にSlackに送られるとこのような感じになります。

Slack通知

これにより、データ量の推移と異常を見逃すことがなくなりました。Anomalyでデータ容量に対してアラートを設定するのもアリかもしれません。

2. 取り込みデータ量の削減

New Relicで取り込みデータ量を削減するには「取り込みデータのサンプリングを実施する」か「Data Drop Ruleを設定してデータの除外設定をする」という2つの方法があります。当時はデータのサンプリングについての検証が行えていなかったため、素直にData Drop Ruleを設定して不要なデータを除外する方向で対応しました。

Data Drop RuleについてはNerdGraphというGraphQLのAPIを利用してデータを除外するためのルールを作成・削除することが可能です。

docs.newrelic.com

NerdGraph API Explorer にアクセスすると下記のような画面が表示されます。

NerdGraph API Explorer

例えばDrop Ruleの一覧を取得したい場合には下記のようなクエリを入力して実行することでDrop Ruleの一覧を取得することが可能です。

{
  actor {
    account(id: <対象となるNew RelicのアカウントID>) {
      nrqlDropRules {
        list {
          rules {
            id
            nrql
            accountId
            action
            createdBy
            createdAt
            description
          }
          error {
            reason
            description
          }
        }
      }
    }
  }
}

また、Drop Ruleを追加したい場合には下記のようなクエリを入力して実行することでDrop Ruleを追加することが可能です。
DROP_DATAは指定したNRQLに該当するデータを除外するもの、DROP_ATTRIBUTESはNRQLに該当する属性・カラムのデータのみを除外することが可能です。

mutation {
  nrqlDropRulesCreate(
    accountId: <対象となるNew RelicのアカウントID>
    rules: {action: <DROP_DATA|DROP_ATTRIBUTES>, description: "<Dropルールの説明>", nrql: "<Dropしたいデータを抽出するためのNRQL>"}
  ) {
    failures {
      error {
        description
        reason
      }
    }
    successes {
      account {
        id
        name
      }
      accountId
      action
      createdAt
      createdBy
      description
      creator {
        id
        name
      }
      id
      source
      nrql
    }
  }
}

しかしNerdGraphはあまり利便性が高いとは言えないため、ここに関してはTerraformで管理することをおすすめします。

# drop_dataの例
resource "newrelic_nrql_drop_rule" "foo" {
  account_id  = 12345
  description = "Drops all data for MyCustomEvent that comes from the LoadGeneratingApp in the dev environment, because there is too much and we don’t look at it."
  action      = "drop_data"
  nrql        = "SELECT * FROM MyCustomEvent WHERE appName='LoadGeneratingApp' AND environment='development'"
}

# drop_attributeの例
resource "newrelic_nrql_drop_rule" "bar" {
  account_id  = 12345
  description = "Removes the user name and email fields from MyCustomEvent"
  action      = "drop_attributes"
  nrql        = "SELECT userEmail, userName FROM MyCustomEvent"
}

Resource: newrelic_nrql_drop_rule registry.terraform.io

Data Drop Ruleの整備により不要なデータを除外することに成功しました。オレンジ色のBrowser eventsの領域が小さくなっていることがわかります。この後に青のMobile eventsを最適化するためにDrop Ruleの調整を行っています。

Plan & usageにてBrowser eventsの減少を確認

フロントエンド系のデータを扱うNew Relicアカウントでデータの減少を確認

BrowserEventsBytesのデータ減少を確認

今後の展望

現在モバイルアプリ開発にて取り込みデータのサンプリングの検証を進めており、Data Drop Ruleとサンプリングの併用でデータ量の最適化を行う予定です。

私達が不要と考えて除外している特定のデータが実はユーザーの体験に関わる問題に関連しており、それを見逃してしまうという可能性を回避したいというのが背景です。本来であれば全てのデータを取得したうえで一律に削減したほうが良いと考えていますが、まだまだ検証中の段階でありリリースは2024年前半の見込みです。

まとめ

TVerにおけるオブザーバビリティの導入・推進の裏側で発生していたデータ量とコストの問題にフォーカスした記事でした。パブリッククラウドSaaSも便利ですが請求に影響するデータのチェックを怠ってはいけません。1つの設定変更で大きな請求が発生してしまう可能性があります。まずはしっかりと状況を可視化したうえで対処していきましょう。

余談ですが、株式会社ヘンリー様のエンジニアブログにある「オブザーバビリティにはお金がかかる」という記事を拝見しまして「私も会社にはNew Relicとオブザーバビリティの重要さについて説明してきたし理解して貰ってると思うけど、とはいえ安くはないし悩ましいよなぁ・・・」という思いから今回の記事の執筆のモチベーションとさせていただきました。

dev.henry.jp

BigQueryのNULLの扱いまとめ

こんにちは、TVerでデータ分析をしている高橋です。
こちらは TVer Advent Calendar 2023 の12日目の記事です
弊社の分析業務は、主にBigQueryに蓄積されたデータを対象としています。データ処理の効率を向上させるため、データの前処理から集計までを一貫してSQLクエリで実施しています。この過程でNULL値の取り扱いは避けて通れない重要なテーマとなっています。
この記事では、(直近タスクでNULL含む処理の検証に多くの時間を溶かした筆者が)弊社で頻繁に使用されるSQLクエリの処理においてNULLがどのように扱われるかをまとめたのでご紹介します。

チートシート

今回調べた内容を整理すると以下の3パターンになりました。

種類 演算子、構文、関数など
NULLとして扱われる 四則演算子, ビット演算子, 比較演算子, 論理演算子,
ARRAY_AGG, FIRST_VALUE, LAST_VALUE, ORDER BY
無視される AVG, MAX, MIN, SUM, STRING_AGG, LOGICAL_AND, LOGICAL_OR, COUNT
FALSE として扱われる CASE, IF

以下、サンプルクエリとともに個別に見ていきます。

演算子

公式ドキュメントには、特に指定がない場合、すべての演算子において被演算子のいずれかが NULL の場合はNULL を返す、と記載されています。

例えば以下のクエリは全てNULL を返します。

SELECT
    -- NULL = 1 <-- これはエラーが発生する
    CAST(NULL AS INT64) = 1,  --> NULL
    CAST(NULL AS INT64) + 1,  --> NULL
    CAST(NULL AS INT64) < 1 , --> NULL

論理演算子の場合は3値論理に基づいて判定が行われます。
演算子NULLを含んでいながらNULLではない値が返ってくるケースがあるようです。

公式ドキュメントから引用

条件式

CASE文FALSENULLは区別されないようです。
以下のクエリを実行すると、すべてのWHEN句でNULLとなるのでunknownが返ってきます。

SELECT
    CASE 
        WHEN NULL AND TRUE THEN "a"
        WHEN CAST(NULL AS INT64) = 1 THEN "b"
        ELSE "unknown"
    END --> "unknown"

IF文も同様です。以下クエリを実行すると2が返ってきます。

SELECT
    IF(NULL, 1, 2) AS col1 --> 2

集計関数

集計関数のうち、AVG, MAX, MIN, SUM, STRING_AGGnon-NULL値を対象として処理が行われるようです。

SELECT
    AVG(val) AS avg_val,    --> 3.0
    MAX(val) AS max_val,    --> 4
    MIN(val) AS min_val,    --> 2
    SUM(val) AS sum_val,    --> 6
    STRING_AGG(CAST(val AS STRING)) AS str_vals --> "2,4"
FROM (
    SELECT
        val
    FROM
        UNNEST(ARRAY[2, 4, NULL]) AS val
)

LOGICAL_AND, LOGICAL_OR も同様にnon-NULL値を対象として処理が行われます。

SELECT
    LOGICAL_AND(flag), --> [TRUE, FALSE] の AND なので FALSE
    LOGICAL_OR(flag)   --> [TRUE, FALSE] の OR  なので TRUE
FROM
    UNNEST(ARRAY[TRUE, FALSE, NULL]) AS flag

COUNT は入力の行数または式がNULL以外の値に評価された行数を取得します。

SELECT
    COUNT(1),    --> 3
    COUNT(flag), --> 2
    COUNT(NULL)  --> 0
FROM
    UNNEST(ARRAY[TRUE, FALSE, NULL]) AS flag

ARRAY_AGG は配列にNULL要素が含まれているとエラーが発生します。
IGNORE NULLSで除外してあげましょう。

SELECT
    -- ARRAY_AGG(val) AS arr           --> Error
    ARRAY_AGG(val IGNORE NULLS) AS arr --> [2,4]
FROM (
    SELECT
        val
    FROM
        UNNEST(ARRAY[2, 4, NULL]) AS val
)

ナビゲーション関数

FIRST_VALUE, LAST_VALUENULL値を含んで計算されます。
例えば、ユーザーが最後に視聴した日付を日次で取得する処理を考えます。
この場合IGNORE NULLSオプションでNULLを無視することで所望の結果を得ることができます。

WITH
play_logs AS (
    SELECT
        "xxx" AS user_id,    
        day,
        logs.begin_d IS NOT NULL AS has_played,
    FROM
        UNNEST(GENERATE_TIMESTAMP_ARRAY(
            TIMESTAMP("2023-12-01 00:00:00"),
            TIMESTAMP("2023-12-05 00:00:00"),
            INTERVAL 1 DAY
        )) AS day
    LEFT OUTER JOIN (
        SELECT
            begin_d
        FROM
            UNNEST(ARRAY[
                TIMESTAMP("2023-12-01 00:00:00"), 
                TIMESTAMP("2023-12-02 00:00:00"), 
                TIMESTAMP("2023-12-05 00:00:00")
            ]) AS begin_d
    ) AS logs
    ON
        day = logs.begin_d
)

SELECT
    *,
    LAST_VALUE(IF(has_played, day, NULL)) OVER(PARTITION BY user_id ORDER BY day ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS day_last_played,
    LAST_VALUE(IF(has_played, day, NULL) IGNORE NULLS) OVER(PARTITION BY user_id ORDER BY day ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS day_last_played_ignore_nulls
FROM
    play_logs

実行結果

ORDER BY句

昇順に並べるとNULLは一番先頭に来るようです。
順序は以下の通り(公式doc)。

はいはいなるほどNULLが最初で...NaN?

NaN (Not a Number)は不正な演算を行った場合に返ってくる値のようです(SELECT IEEE_DIVIDE(0, 0)NaNを返す)。
NULLS LASTオプションでNULLを末尾に追いやることが可能です。

SELECT
    val
FROM
    UNNEST(ARRAY[1, 3, 2, NULL]) AS val
ORDER BY 
    val NULLS LAST

1が先頭になりました

まとめ

  • NULL含む論理演算と条件式は難しいのでNULL埋めしてあげたい
  • ORDER BY句のNULLS LASTは便利そう

最後に

ニッチ感の否めない記事となりましたが、このようにTVerではデータ分析を通じてより確からしい示唆を得ようと日々精進を重ねています。
もしご興味があれば以下よりご連絡ください。お待ちしております!

recruit.tver.co.jp

Inter BEE 2023 参加レポート & Media-JAWSを開催しました #interbee #mediajaws

こんにちは、TVerの加我です。
こちらは TVer Advent Calendar 2023 の4日目の記事です。

先日Inter BEE 2023に併せてMedia-JAWSを開催しましたのでそちらのレポートになります。

昨年のレポートはこちら。

techblog.tver.co.jp

Inter BEE 2023

今年は11/15 - 11/17にかけて幕張メッセで開催されました。Inter BEE自体の説明については昨年のレポートをご覧ください。

会場

昨年は予備知識というか業界知識がゼロの状態で参加したので展示を見ても何が何やらという状態でしたが、今年は多少なりとも知識があったので展示機材を見て「それが何であるか」程度は理解できました。とはいえ、放送に関する技術要素を体系的に学んだわけではないので「名前だけは聞いたことがある」程度のレベルのままですが・・・。

実際に納品されたというサブ (デジタルミキシングコンソール)

いつもお世話になっているNew Relic様のブース

ちょっと気になった在京放送局システム会社アライアンス

在京放送局システム会社アライアンスについて補足しておきますと、放送関連のシステムなどで各局が協力できる領域についてはみんなで協力してDX推進を行い、そのうえでコンテンツで勝負しようというのがコンセプトの団体とのことです。

現在の放送局システム会社にはいくつかの共通して抱えている課題があります。人的リソースからITインフラに至るまで、広範囲の課題をスピード感を持って解決するには限界があります。この背景から、各社との協調領域を定義し、協力体制を構築する試みを開始しました。その第一歩として『在京放送局システム会社アライアンス』をスタートし、新機軸と最適化軸の両輪で放送業界のDX推進を目指します。

newscast.jp

セッション (1)

AWS様のブースにてブライトコーブ様との対談形式のセッションがあり、弊社から2名が登壇いたしました。

対談セッション

ブライトコーブ様との対談形式セッション

11/15と11/17はモバイルアプリとWebアプリケーションのディレクターを務める岡田 銀平が担当し、11/16はAndroidTV・FireTVといったコネクテッドTV (CTV) のディレクターを務める若林 紘大が担当しました。

ディレクターの岡田 銀平

ディレクターの若林 紘大

セッションでは「導入前の課題とブライトコーブを選んだ理由」「ブライトコーブ導入後の放送局との連携に関する変化」などについて対談が行われました。お二人ともお疲れ様でした!

セッション (2)

11/15に民放技術報告会 企画セッションにて「CTV上の諸技術に関する研究と考察」というセッションを聴講しました。

技術的な側面からコネクテッドTVを分析し、ユーザー体験など将来的にどうあるべきなのかといった話が勉強になりました。セッションでは米国FASTや、英国Freeviewなどを参考にさせて頂いたとのことです。
残念ながらこちらのセッションについてはアーカイブ配信は行われていませんが、主な内容は「放送技術誌 (兼六館出版) 11月号」にまとめられています。

www.inter-bee.com

まいとうさんによるCTVの技術に関する研究と考察

CTVデバイスの視聴時間割合は増えているとの調査結果

放送についてはARIB等の標準規格があるが、CTVのアプリの機能については各ベンダーの仕様に基づくサービスが展開されている

セッション (3)

11/17にNew Relic様の出展者セミナー 企画セッションにて「TBS IDの構築によるサービス統合とグループ全体のシナジー: VISION2030に向けた内製化への挑戦」というセッションを聴講しました。

TBS様がTBS IDを開発・運用するにあたってNew Relicをどのように導入したのか、オブザーバビリティがどのように内製化に寄与したのかをお話頂きました。最後にQ&Aにて質問させていただいたのですが、丁寧に回答いただきありがとうございました。

www.inter-bee.com

Media-JAWS #14

昨年に引き続き今年もInter BEE 2023に併せてMedia-JAWSを開催しました。思えば昨年11月に開催されたMedia-JAWSから運営に携わることになりあっという間の1年でした。

media-jaws.doorkeeper.jp

昨年はAWS様のプライベートセミナーとして会議室を使わせていただいたのですが、今年は自分たちで会場を探すことになり海浜幕張付近の貸し会議室を検討することになりました。いくつか候補を挙げた中から「キャパシティと予算感が適当で、かつ会場と同じビルの居酒屋で懇親会ができる」という理由でイオンコンパス幕張会議室を利用させていただきました。

www.aeoncompass-kaigishitsu.com

今回の開催ではTVerで会場スポンサーをさせていただいたのと、バックエンドエンジニアの伊藤がJAWS-UGでの初登壇を成し遂げました。登壇の詳細については後日こちらのブログにて共有して貰える予定です。

伊藤については4月のAWS Summit Tokyo 2023に参加したときにJAWS-UG周りの人と話して良い刺激を受けたようで、今回の登壇について相談した際に快く引き受けてくれました。非常に嬉しく思います。

会場スポンサーをさせていただきました

TVerバックエンドエンジニアの伊藤

そして、今回のInter BEE + Media-JAWSに併せてAWSのIVS (Amazon Interactive Video Service) のチームが来日し登壇していただけることになりました。運営メンバーの1人としてこの場を借りてお礼申し上げます。

過去にオンラインで開催されたJAWS-UGのイベントにてAmazon IVSを利用していること、JAWS-UGの中でもメディア系企業の登壇が多いMedia-JAWSAmazon IVSとの親和性が高いとの理由からコラボレーションに至りました。ステッカーやTシャツ、靴下など大量のノベルティを持参していただきイベントを盛り上げてくれました。改めてありがとうございました!

IVSステッカー

IVS Tシャツ

IVSチーム

IVチームのHaoさんとToddさん

Amazon IVSリアルタイムストリーミングのデモ

今回のMedia-JAWSでは北海道文化放送様と静岡放送様に初登壇していただけました。

北海道文化放送の杉本さんとは今年の夏季休暇で札幌に帰省したときに知り合ったのですが、先日11月4日に札幌で開催された AWSカーニバル でお会いした際に「Inter BEE参加されますか?Media-JAWS興味ありませんか?もしよければ登壇いかがですか?」といった若干強引な流れでお誘いしまして登壇していただけました。

北海道文化放送の杉本さん

杉本さんの登壇内容は個人的にとても魅力的だと感じていまして「弊社ではまだFAXを使っています」からの「FAXで送られてきた原稿をFAXをOCRで文字起こししてAmazon Bedrockで原稿を作成し、手直ししたものをPollyで音声化してYouTubeでAI自動音声ニュースとして配信してます」というレガシーとモダンの温度差にやられそうになりました。杉本さんは純粋なエンジニアではなく、しかもAWS歴は2年とのことで強い衝撃を受けました。

speakerdeck.com

静岡放送様ではコミュニケーション改善のためのサービス開発をされている話や、放送局とVTuberがコラボして地元の盛り上げに貢献していたりテレビ局のオープニング映像にも起用しているという面白い話がありました。後者は直接的な技術的の話ではありませんが、放送局が手掛けるビジネスの一環ということで興味深く聞かせていただきました。

静岡放送 高橋さん

静岡放送 牧野さん

www.screens-lab.jp

www.youtube.com

ちなみに静岡放送様はコールサイン (テレビ) が JOVR-DTV ということもあってVTuberっぽい響きであることに局所的な盛り上がりがありました。放送業界2年目の私はすぐに反応できずまだまだ未熟であることを実感しました。

懇親会にはなんと40名前後の方が参加してくれまして、登壇内容についての話やら次回のMedia-JAWS開催地の話などで盛り上がりました。来年の開催については既にいくつか候補がありまして、今年に引き続き来年も精力的に活動していく予定ですのでご期待ください。

まとめ

今年もInter BEEは大盛況で、そこに併せて開催したMedia-JAWSも多くの方に参加していただけました。

昨年は業界に知り合いがおらず肩身の狭さと若干の居心地の悪さを感じたものですが、Media-JAWSを通じて放送業界の方々と知り合えたおかげで今年のInter BEEでは多くの人と交流することができました。引き続きメディア業界の盛り上げと技術発信に貢献するため頑張っていく所存です。

#ISUCON13 に パカパカアルパカとして参加して22位でフィニッシュでした!(86,322点)

こんにちは!

こちらは TVer Advent Calendar 2023 の2日目の記事です。

TVerのサービスバックエンドのリードエンジニアをやっております内海です🐶!

今年も昨年同様、チーム:パカパカアルパカとして出場してきました。

isucon.net

22位 86,322 パカパカアルパカ

いえーい🎉

やったこと

担当範囲

基本、私はいつもアプリケーションレイヤーばかりみています。

toritori0318氏がインフラをメインに担当。 teraken氏がツール周りと遊撃担当。

開始直後

  • AWS上のリソース構築
  • レギュレーション、マニュアルの読み合わせ

リソース構築完了後

  • 初回ベンチ実行
  • 各リソースの負荷状況確認
  • マニュアルからトピックの抜き出し

このタイミングではまだやれることが少ないポジションなので、マニュアルから課題になりそうなトピックを抜き出してまとめていました。

また負荷状況はどうみてもMySQLボトルネックになっていることを確認。

どうせ分散していかなきゃいけないことは明白だったため、一旦1台でいけるところまでチューニングをしてから負荷状況に合わせてサーバーを割り当てる作戦でいくことにしました。

icon対応

マニュアルと読んでいると If-None-Match ヘッダーに対しての記載が丁寧だったので、まず深追いせずに

  • MySQLにデータ保持をしているiconを静的ファイル化
  • hash値をredisで保持
  • If-None-Match ヘッダーがある場合304を返すように変更

スコア下がる 🤔

PowerDNS

DNS周りの運用は経験がなかったので、問題をみた瞬間にすっぱり切り捨てていた課題だったのですがTTLが0になっていることに気づいて対応をいれました

ここでお昼前5,000弱

モデレートAPIのN+1解消

これでもかってくらいN+1だらけだったので、どこから手をつけるか悩んでましたが

配信品質が上がると視聴者数があがるというマニュアル記載があったため、ブロッカーになりそうなモデレートAPIから着手しました

themeの廃止

ユーザーのダークモード設定が別テーブルで毎回呼ばれていたためusersテーブルに移行し、UserModelで保持できるようにしました

ここでやっと6,801。どのメンバーの対応も微増ではありつつも、着実にスコアは伸びていきました。

作戦会議

そろそろいろんな箇所の課題が見えてきたので一旦ホワイトボードに現在の構成などを書きながら、それぞれの気づきと方向性の共有を行いました

一旦昼過ぎのこのタイミングでは

  • Nginxでiconをちゃんと返すようにする
  • DNSサーバーとAppサーバーはわけたい

という方針になりました

iconをNginxでキャッシュするように変更

toritori0818氏がNginxでキャッシュできるように対応してくれて 8,796 点

まだここでも1サーバーしか使ってない状況です。

そろそろ分散して課題の深掘りにフェーズ移行していこうと言う話になりました。

MySQLサーバー

2台目のサーバーをMySQL専用サーバーに変更しました

このときは

BenchTarget: Server1
Server1: PowerDNS / App(Go) / redis / Nginx
Server2: MySQL
Server3: 

という状況になりました。

ここでスコアが伸びて 18,181 🎉

ここからはN+1を潰しつつ、それぞれの負荷状況を見て、分散構成を考えていました。

結果的に

サーバー
BenchTarget: Server1
Server1: PowerDNS / App1(Go) / Nginx
Server2: MySQL
Server3: App2(Go) 

アプリケーション
App1: icon / dns系APIのみ
App2: その他

という構成でわけました。

Server1にPowerDNSが居たので、できるだけ触らなくていいようユーザー登録APIをServer1:App1で受け付けることにしましたw

また、それだけだと少しApp1が余るので、比較的CPU負荷の少ないiconAPIもApp1に同居させました。

構成を変えてスコアは 27,702 🎉

最後の追い込み

  • 出来うるN+1を解消していく
  • iconのBinaryをそのままMySQLに書き込んでいたのをやめる
  • iconAPIでApp(Go)までたどり着いた場合、原則的にicon未設定者と断定してfallbackImageを即レスするように

などなどやっていると、めきめきとスコアがあがっていきました💪

午前中にやっていた細々したものが開花してきた感があって、アドレナリンでちゃう感じでした!

感想

ISUCONに出場すると毎年思うのですが、

  • 瞬間的な取捨選択の嗅覚
  • わからない状況でも前にコケる練習
  • Webエンジニアとして足りていない部分の再確認

というなかなか普段の業務では獲得し辛いスキルが磨かれるなぁ。と感じています。

日々の業務でもきっと活きてくるスキルだと思いますので、是非ISUCONに参加してみてはいかがでしょう。

来年(まだあるかわからないですが)、対戦出来ること楽しみにしております 😘

運営のみなさん、参加者のみなさん本当にありがとうございました。今年も最高でした!!!!

ISUCON13の問題や、ベンチマーカーの実装は

github.com

こちらで公開してくださっています

後日談

再起動テスト終わって、提出スコア出すためのベンチしてたんですが…

なぁんかベストスコアでないんだよなぁ。と思ってました。

終わってからリポジトリみてみると、マージされてないPullRequestが残ってました😇(ログの出力消したりするくらいのやつ

みなさんも、ちゃんとPRがMergeされてるかは確認しましょう😇

「TVer Advent Calendar 2023」開催のお知らせ

こんにちは、TVerの加我です。

昨年に引き続き今年もTVer Advent Calendarを開催します!
こちらはTVer Advent Calendar 2023 の1日目の記事となります。

qiita.com

昨年のはこちら。

qiita.com

今年のアドベントカレンダーに向けて

現在のTVerは下記のような組織体制となっております。

全体の組織図

speakerdeck.com

その中でサービスの開発に携わっている部門 ≒ 開発組織は主に下記が挙げられます。

  • TVerの開発・運用を担っているサービスプロダクト本部
  • TVer広告プラットフォームの開発・運用を担っている広告事業本部

昨年は現サービスプロダクト本部のエンジニアがメインでアドベントカレンダーの執筆に取り組んでいたのですが、なにしろTVerの開発組織が発足し動き出した年だったので執筆者が少なく、どのような記事を出そうかみんなで頭をひねったりもしました。

しかし今年の開発組織は昨年に比べて約2倍の人数になり、TVer広告プラットフォームの開発・運用に関わる広告事業本部にもエンジニアが増えてきているのでバラエティ豊かな記事が出てきそうな気配があります。

ということで

初日は私がアドベントカレンダー開会の儀を執り行わせていただきました。

明日はバックエンドエンジニアである内海が先日開催されたISUCON13について話してくれますのでお楽しみに!