Splunk Certified Power Userを受けてきた

Splunk Certified Power Userを受けてきました。

どういった試験だったのか、どういう流れだったのかを残すために備忘録として残しておきます。

なぜ受けたのか

転職した先の会社の研修の一環でSplunk Certified Power Userを受けてきました。

どういった試験なのか

試験の対象者は?

Splunkが出している認定資格のうち入門レベルの資格試験となります。

対象者はSplunkに触り始めて、SPLをよりもっと効率的に書きたいという人むけの試験かと思います。

詳細は以下のホームページを見てください

www.splunk.com

テストを受けるまでの流れについて

テストを受けるためには、PearsonVueにてアカウントを作る必要があります。

既存のPearsonVueをそのまま使うことはできず、Splunk IDというIDと紐づいたアカウントを作る必要があります。

このSplunk IDは申請したらすぐにできるわけではなく、ある程度の時間がかかります。

そのため自分のように衝動的に日程を決めようと思う人の場合は、注意してください。

もし2日程度経ってもSplunk IDが発行されない場合はSplunk側に連絡しましょう。

home.pearsonvue.com

 

テスト内容について

よく使うだろうSPLについての問題やKnowledge Objectについての問題がほとんどとなります。ちゃんと研修も受けずにとりあえずSplunkを使ってきた人にはちょうどいい試験という印象です。

最後に

Splunkを会社で導入していて触っているけどいまいち使い方がよくわからないという人には丁度いい試験だと思います。

 

Cypressでtd-js-sdkのテストをする

Tagのテストをしたい

td-js-sdkをWeb Siteに設置したはいいけどテストが手動だったり、そもそもテストをしていなくてしばらく経ってから想定通りに動作してないことが発覚して修正に手間取ったりとかがわりと起こりがちである。

Cypressを使ってこういう自体を防ぐ手法を案内したい。

Cypressとは

CypressはJavaScriptのE2Eテストフレームワークである。

npmで簡単にインストールもできるし、テストの内容も簡単にかける。

詳細については公式のページをみてほしい。

www.cypress.io

環境構築

npmでcypressをインストール

npm install cypress

testディレクトリを作成

mkdir td-sdk-cypress && cd $_

ディレクトリを作成

npx cypress open

testは上のコマンドで生成されたcypress/integrationに書いていく。

テストコード

it('td-sdkが読み込まれていること', () => {
    cy.visit('/');
        
    cy.get('head script[src*="td.min"]')
});

it('td-client-idが生成されていること', () => {
    cy.visit('/');
    cy.getCookie('_td')
      .should('exist')
});

it('td-global-idが生成されていること', () => {
    cy.visit('/')
    cy.getCookie('_td_global_id')
});

cypress.jsonにbashUrlを設定しておくとテストコードを設定しておくといちいちURLを記述せずにパスだけを指定するだけで良くなる。今回はそれにしたがっている。

{
    "baseUrl": "https://www.treasuredata.co.jp"
}

テスト実行

npx cypress open

上記を実行すると自動的にcypressのアプリケーションが起動される。

f:id:mickeeey:20220116222221j:plain

Runを押して、テストを実行しよう。

すると自動的にブラウザが起動してテストが開始される。 またそのテストの様子は同ディレクトリにmp4ファイルとしても生成され、テスト結果の確認にも使える。

f:id:mickeeey:20220116222415j:plain

終わりに

cypressを使うことでこれまでテストとして組み込みにくいタグのテストを自動化できることがわかった。積極的に布教して行って、正しく実装されたtd-js-sdkの使用が広まることを願っている。

Deno向けのTreasure Data Clientを作った話

なんとなくDenoとかTypeScriptに触りたくて、Treasure Data向けのSDKを作った。(需要があるかどうかはしらないが、作りたかったので仕方ない)

ソースコード

ソースコードはこちらです。

使い方はexampleにもあるんですが、こんな感じです。

import { TDClient } from "../mod.ts";

const apiKey = Deno.env.get('TD_API_KEY') as string
const td = new TDClient('https://api.treasuredata.com', apiKey)
try {
    let query = await td.query('presto', 'sample_datasets', 'select * from www_access limit 100')
    let job = await query.json()
    let jobStatus = await td.getJobStatus(job.job_id)
    let status = await jobStatus.json()

    for(;;) {
        if (status.status === 'error' || status.status === 'success'){
            break
        }
        jobStatus = await td.getJobStatus(job.job_id)
        console.log(`${job.job_id} status : ${status.status}`)
        await td.sleep(3)
        status = await jobStatus.json()
    }

    console.log(status.num_records)
    let result = await td.getJobResult(status.job_id, 'tsv')
    console.log(await result.text())

    result = await td.listDatabases()
    console.log(await result.json())

    result = await td.jobList()
    console.log(await result.json())
} catch(e) {
    console.log(e)
}

どうしてDenoなのか

TypeScript自体は前から興味があったのですが、トランスパイルの仕組みがあまり好きになれず積極的に学ぶ気にはなれませんでした。
一方でDenoはネイティブでTypeScriptをサポートしており、上記の煩わしさが大幅に改善されたJavaScript/TypeScript環境で非常に気に入ってます。
その理由もあり、通常のTypeScript向けのSDKではなくDeno向けとしてとりあえず作った次第です。

今後について

Queryを投げたりはできるようにしてますが、それ以外の機能はまだ未実装です。
この公式ドキュメントを参考にして少しずつ機能を実装していければ、と考えてます。

docs.treasuredata.com

Treasure DataのJobの状態を監視するPromethesuのExporterを作った

経緯

Treasure DataのJobの同時実行数には上限があり場合によっては不要なJobをKillするなどの対応をする必要があるが、そもそも現状のコンソール経由だと実際にどれくらいのJobが実行中なのかがぱっと見わからない。

API経由でJobの状態を取得可能なのでとりあえず、それを取得するようなExpoterを作ることにした。

 

成果物

github.com

apikeyを指定してexporterを実行すると以下のようなメトリクスが取れる。

あとはこれをPrometheusでscrapeしてくれればどれくらいのJobがどの時間帯に多く実行されているかがわかるようになる。

 

f:id:mickeeey:20201206185222j:plain



 

exporterを自作するにあたってこちらの記事を参考にさせてもらった。

 

ryo-xjsbx.hatenablog.com

感想

簡単に作れて楽しかったです。

 

TreasureDataのSQLでパラメーターを使いたい

1. はじめに

ここでいうSQLでパラメーターが使いたいというのは以下のようなSQLを意味します。

SELECT * from www_access WHERE code=@code

残念ながらTreasure DataのSQLは単体でパラメータ(変数)を扱うことができません。

注意: Presto自体にPrepareとExecuteという機能があり、上記のようなパラメーター化されたクエリを実行することは可能なのですが、Treasure Dataでは利用できません。 PrestoのPrepareとExecuteについてはこちらをご確認ください。 prestosql.io

ではどうしたら良いのでしょうか?

2. Workflowを使う

Treasure Data WorkflowはTreasure DataによってホスティングされているDigdagサービスです。
Digdag自体には変数を扱うことができる機能があるので、その機能を利用してクエリをパラメーター化してみましょう。

_export:
  td:
    database: sample_datasets
  code_parameter: 200

+paramtered_query:
  td>:
  query: "SELECT * FROM www_access WHERE code=${code_parameter}"

_exportで宣言した変数をquery内で呼び出して使用しております。

定常的に実行されるクエリならばこれで良いかもしれません。ただマーケティング部門のユーザーに使ってもらう時にはymlを編集してもらうというのはやや抵抗感があるかもしれません。

そういう場合にはGoogle Colboratoryを使うのはいかがでしょうか?

3. Google Colaboratoryを使う

Google ColaboratoryはGoogleホスティングしてくれているJupyter Notebookサービスです。そこにTreasure DataのSDKである、td-client-pythonをインストールして簡易的なTreasure Dataへのインターフェースにしまえという発想です。

f:id:mickeeey:20200810173341j:plain

import os
from getpass import getpass

os.environ['TD_API_KEY'] = getpass('Enter TD API KEY ')

os.environ['TD_API_SERVER'] = 'https://api.treasuredata.com'

code_parameter = input('code_paramter =')
mport tdclient

query = f"SELECT * FROM www_access WHERE code={code_parameter}"

with tdclient.Client() as td:
  job = td.query("sample_datasets", query, type="presto")
  job.wait()
  for row in job.result():
      print(repr(row))

4. まとめ

  • TreasureData単体ではパラメーター付きSQLは実行できない
  • WorkflowやClient SDKを組み合わせる事で実現することは可能。

GoのInterfaceについての個人的まとめ

GoのInterfaceについて

A Tour of Go

上記のページからの引用だがA tour of Goでは以下のように記載されている。

interface(インタフェース)型は、メソッドのシグニチャの集まりで定義します。
そのメソッドの集まりを実装した値を、interface型の変数へ持たせることができます。 

これだけだと個人的にはわかりにくかったが実装すべき関数interface型に定義し、それを実装したstructはinterfaceの型としても扱えるような記法。
こうすることで同じような関数を実装する必要がなくて、コードの記述量を減らすことができるという点がメリットと理解している。

package main

import "fmt"

type englishBot struct{}
type spanishBot struct{}

type bot interface {
    getGreeting() string
}


func printGreeting(b bot) {
    fmt.Println(b.getGreeting())
}
// getGreeting()を実装しているので
// bot型としても扱える
func (englishBot) getGreeting() string {
    return "Hello there"
}


// getGreeting()を実装しているので
// bot型としても扱える
func (spanishBot) getGreeting() string {
    return "Hola"
}

func main() {
    eb := englishBot{}
        // englishBotはbot型でもあるのでprintGreeting関数の引数と渡せる。
    printGreeting(eb)
    sb := spanishBot{}
    printGreeting(sb)
}

interface{}型について これは結局何も関数を定義していない結果、どんなデータ型でもあるという状態。 どんなデータ型が入ってくるかわからない時や、入ってきたデータの型を判別する時に利用すると良い。

GoでのNodeの扱い方

TreeNodeの次をとる方法。

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func getDecimalValue(head *ListNode) int {
    curr, result := head, 0
    for curr != nil {
        result = result<<1
        result += curr.Val
        curr = curr.Next
    }
    return result
}