ListのListで、前後の差分のListをつくる

状態遷移の記録をしたいとき、例えばこういうリストがあるときに

@ val l = List(List(1, 2, 3, 4, 5), List(3, 4, 5), List(5))

5個あった要素が、3個になり、1個になった、という遷移を、

  • 2番目の処理で1と2が落ち、
  • 3番目の処理で3と4が落ちた

というように表現したいとき。

@ (l(0) :: l, l).zipped.map(_ diff _)
res3: List[List[Int]] = List(List(), List(1, 2), List(3, 4))

要素を一個ずつずらしたリストと元のリストをタプルにして、zipしてそのdiffをとる。 もっとうまい方法があるかも。

参考:

How to map a list of numbers to the list of their deltas in Scala? - Stack Overflow

nginx-backend間でKeepAliveする

nginxをフロントにおいて、バックエンド(upstream)にリバースプロキシするというのはよくあるけど、nginx - upstreamでkeepaliveさせるには設定がいる。数年前にやっていたのに忘れていたのでメモ。

公式のドキュメントはこちら。 Module ngx_http_upstream_module

公式のママだが、(1)httpバージョンを1.1にする (2)Connectionヘッダを空にする、の2つの設定が必要となる。

upstream http_backend {
    server 127.0.0.1:8080;

    keepalive 16;
}

server {
    ...

    location /http/ {
        proxy_pass http://http_backend;
        proxy_http_version 1.1;  // http1.1する=デフォルトでKeepAliveする
        proxy_set_header Connection ""; //nginxはデフォルトでcloseを入れてしまうので空にする
        ...
    }
}

参考

blog.nomadscafe.jp

qiita.com

コネクションを毎回新規生成しなくなるのでスループットも上がる。

http1.1ならデフォルトでKeepAliveする、というのもすっかり忘れていた。 http2だとどうなるんだろう。

libmysqlclient-devを入れる

debianを使う。 pythonmysqlを使うのに必要というので、

$ sudo apt-get install libmysqlclient-dev
Reading package lists... Done
Building dependency tree
Reading state information... Done
Package libmysqlclient-dev is not available, but is referred to by another package.
This may mean that the package is missing, has been obsoleted, or
is only available from another source

E: Package 'libmysqlclient-dev' has no installation candidate

無い。普通にぐぐるとあるはずという。

www.linuxquestions.org

sudo apt-get install default-libmysqlclient-dev

で入れることができた。

Stackdriver Traceを使ってみる(準備)

何らかのAPM(Application Performance Management)ツールを入れたい。以前はNew Relicを使っていたことがあるけど、サーバー(というかCPU)の数に応じて費用がどんどん高騰していくし、かといって以前やっていたように複数台のうちの1台に入れる、というのは出し分けの設定が面倒。

そこでGCPにあるStackdriver Traceを使ってみたい。

Google Cloud Platform のお客様は、Stackdriver Trace を追加料金なしでご利用いただけます。

まずどのライブラリを使えばよいのかがわかりづらかった。

(1) https://github.com/GoogleCloudPlatform/cloud-trace-java/

(2) https://github.com/GoogleCloudPlatform/google-cloud-java/tree/master/google-cloud-trace

なんか似たようなレポジトリがある。 これについては、ざっと中身を見たのと、下記のPRを発見(なぜ取り込まれてない...)

Directs users to cloud-trace-java for instrumentation by mtwo · Pull Request #2382 · GoogleCloudPlatform/google-cloud-java · GitHub

this library allows you to interact with the Stackdriver Trace API, but is not intended for instrumenting an application. If you are looking to send traces to your app, please use the cloud-trace-java SDK.

なのでGoogleCloudPlatform/cloud-trace-javaを使う。

ライブラリの依存関係がきつい。実は(2)のほうも依存ライブラリになっていて、(2)のバージョンが"0.24-alpha"指定になっている。

cloud-trace-java/pom.xml at 0.5.0 · GoogleCloudPlatform/cloud-trace-java · GitHub

最新は0.30で、他で使っていたものと競合してしまったが、いったん0.24のほうで全部合わせた。

500エラーで悩んだときのログ、REPLで試したところ、下記のエラー。

scala> import com.google.cloud.trace.service.TraceGrpcApiService
import com.google.cloud.trace.service.TraceGrpcApiService

scala> val traceService = TraceGrpcApiService.builder().setProjectId("xxxxxxxxx").setScheduledDelay(1).build()
java.lang.NoClassDefFoundError: com/google/api/gax/grpc/ChannelProvider
  at com.google.cloud.trace.service.TraceGrpcApiService.<init>(TraceGrpcApiService.java:162)
  at com.google.cloud.trace.service.TraceGrpcApiService.<init>(TraceGrpcApiService.java:43)
  at com.google.cloud.trace.service.TraceGrpcApiService$Builder.build(TraceGrpcApiService.java:141)
  ... 39 elided
Caused by: java.lang.ClassNotFoundException: com.google.api.gax.grpc.ChannelProvider
  at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
  at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
  at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
  ... 42 more

実行時エラーになってしまっていた。これは google-cloud-javaのバージョンを0.30を入れたときに出ていて(0.30と0.24が競合したときに0.30が採用されていた)、このエラーが出る。

そのエラーが出なくなって次に直面したのがこれ。

Welcome to Scala 2.12.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_152).
Type in expressions for evaluation. Or try :help.

scala> import com.google.cloud.trace.service.TraceGrpcApiService
import com.google.cloud.trace.service.TraceGrpcApiService

scala> val traceService = TraceGrpcApiService.builder().setProjectId("xxx").setScheduledDelay(1).build()
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGBUS (0xa) at pc=0x00000001271f5e40, pid=6617, tid=0x000000000000601b
#
# JRE version: Java(TM) SE Runtime Environment (8.0_152-b16) (build 1.8.0_152-b16)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.152-b16 mixed mode bsd-amd64 compressed oops)
# Problematic frame:
# C  0x00000001271f5e40
#
# Core dump written. Default location: /cores/core or core.6617
#
# An error report file with more information is saved as:
# /Users/xxxx/Documents/xxxxx/xxxxx/hs_err_pid6617.log
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
/usr/local/Cellar/sbt/0.13.15/libexec/bin/sbt-launch-lib.bash: line 46:  6617 Abort trap: 6           (core dumped) "$@"

コアダンプを吐いて落ちてしまっている。 ただこれはMacだからではないか(Linuxなら問題なくできるのでは)と思いやってみたらLinuxでは動いた。

Terminatingのまま消えてくれないPodを強制的に削除する

podがいくら待っても消えてくれないとき。

stackoverflow.com

--force --grace-period=0をつける。

例:

ᐅ kubectl get pods                                                               
NAME                                             READY     STATUS        RESTARTS   AGE
foo-deployment-2799137922-7xc7r           2/2       Running       0          27d
bar-deployment-2705092949-mmkf1             3/3       Terminating   0          1h
bar-deployment-2705092949-nk366             0/3       Pending       0          1m
ᐅ kubectl delete pod --force --grace-period=0 bar-deployment-2705092949-mmkf1           
warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "bar-deployment-2705092949-mmkf1" deleted

即消えてくれる。 あんまりやりたくないが、どうしてものとき。

http4sをHTTPクライアントとして使う

http4s: HTTP Client

にあるとおり。

build.sbt

scalaVersion := "2.12.4"

val http4sVersion = "0.17.5"

libraryDependencies ++= Seq(
  "org.http4s" %% "http4s-dsl" % http4sVersion,
  "org.http4s" %% "http4s-blaze-server" % http4sVersion,
  "org.http4s" %% "http4s-blaze-client" % http4sVersion
)

あとはsbt consoleで試してみる。 

scala> import org.http4s.client.blaze._
import org.http4s.client.blaze._

scala> val httpClient = PooledHttp1Client()
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
httpClient: org.http4s.client.Client = Client(Kleisli(org.http4s.client.blaze.BlazeClient$$$Lambda$1715/1558206896@39c1f446),Task)

fs2.Taskとして返ってくるのでrunすれば結果を得られる。

scala> val yahoo = httpClient.expect[String]("https://www.yahoo.co.jp")
yahoo: fs2.Task[String] = Task

scala> yahoo.unsafeRun
res2: String =
"<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta http-equiv="content-style-type" content="text/css">
<meta http-equiv="content-script-type" content="text/javascript">
<meta name="description" content="日本最大級のポータルサイト。検索、オークション、ニュース、メール、コミュニティ、ショッピング、など80以上のサービスを展開。あなたの生活をより豊かにする「ライフ・エンジン」を目指していきます。">
<meta name="robots" content="noodp">
<meta name="google-site-verification" content="fsLMOiigp5fIpCDMEVodQnQC7jIY1K3UXW5QkQcBmVs">
<link rel="canonical" href="https://www.yahoo.co.jp/" />
<link rel="alternate" media="only screen and (max-width: 640px)" href="https://m.yahoo.co.jp/">
<link rel="alternate" href="an...

java.util.loggingのログをStackdriverに出力する(続き)

前回の続き。

曲がりくねった方法をとってしまったが、java.util.loggingの仕様を順に追って検討すればできた。

java.util.loggingのログの出力の設定は、

  • java.util.logging.config.class のシステムプロパティの定義があればそれを参照
  • java.util.logging.config.fileのシステムプロパティの定義があればそれを参照
  • どちらも無ければ ({JRE}/lib/logging.properties) の定義をみにいく

という動きをする。なので java.util.logging.config.class を定義する方法を取った。

LoggingConfig.scala

package com.test

import java.io.InputStream
import java.util.logging._

import ch.qos.logback.core.CoreConstants

class LoggingConfig() {
  try {
    val property: InputStream = getClass.getResourceAsStream("/logging.properties")

    if (property != null) {
      LogManager.getLogManager.readConfiguration(property)
    }
  } catch {
    case e: Exception =>
      e.printStackTrace()
  }
}

class StdOutHandler extends StreamHandler {
  val envLogLevel: Level = sys.env.get("LOG_LEVEL") match {
    case Some("ALL")   => Level.ALL
    case Some("TRACE") => Level.FINEST
    case Some("DEBUG") => Level.CONFIG
    case Some("INFO")  => Level.INFO
    case Some("WARN")  => Level.WARNING
    case Some("ERROR") => Level.SEVERE
    case Some("OFF")   => Level.OFF
    case _             => Level.ALL
  }
  setOutputStream(System.out)
  setLevel(envLogLevel)
  setFormatter(new CustomFormatter)
}

class CustomFormatter extends Formatter {
  override def format(record: LogRecord): String =
    s"""{
       |  "severity" : "${record.getLevel}",
       |  "message" : "(${record.getLoggerName}) ${formatMessage(record)}"
       |}
       |""".stripMargin.replace("\n", "") + CoreConstants.LINE_SEPARATOR
}

logging.properties

handlers=com.test.StdOutHandler
.level=ALL

本当はLoggingConfig.scalaだけで完結させることができるのかもしれないが、どうやればいいのかわからなかったため、一部をlogging.propertiesに残している。 これで、java.util.loggingを使って出力されるログをGCPのStackdriver Loggingに合わせたフォーマットで出力できるようになった。

なお、logbackでやる方法はこちら。

github.com