'2017/11/06'에 해당되는 글 2건

  1. 2017.11.06 lazy val의 내부(volatile/synchronized)
  2. 2017.11.06 docker container 로그 보기



lazy val에 대해 잘 설명된 블로그 글이다.


https://blog.codecentric.de/en/2016/02/lazy-vals-scala-look-hood/




스칼라의 LazyCell 클래스에는 lazy val이 있다. 



final class LazyCell {
  lazy val value: Int = 42
}


자바로 디컴파일 해보면 아래와 같이 변환된다고 블로그 글에 나와 있다.


final class LazyCell {
  @volatile var bitmap_0: Boolean = false                   // (1)
  var value_0: Int = _                                      // (2)
  private def value_lzycompute(): Int = {
    this.synchronized {                                     // (3)
      if (!bitmap_0) {                                      // (4)
        value_0 = 42                                        // (5)
        bitmap_0 = true
      }
    }
    value_0
  }
  def value = if (bitmap_0) value_0 else value_lzycompute() // (6)
}


scala 2.12로 컴파일하고 실제로 jad 로 디컴파일하면 다음과 같다. 거의 동일하다.


내부적으로 volatile과 synchronized를 사용한다. 즉 multiple thread에서 동기화가 보장되도록 되어 있다! 예제2에서 설명하고 있다. 



// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.

// Jad home page: http://www.kpdus.com/jad.html

// Decompiler options: packimports(3)

// Source File Name:   LazyCell.scala



public final class LazyCell

{


    private int value$lzycompute()

    {

        synchronized(this)

        {

            if(!bitmap$0)

            {

                value = 42;

                bitmap$0 = true;

            }

        }

        return value;

    }


    public int value()

    {

        return bitmap$0 ? value : value$lzycompute();

    }


    public LazyCell()

    {

    }


    private int value;

    private volatile boolean bitmap$0;

}





예제 1이다. 참조 블로그의 1번째 예제를 repl에서 실행해 본다.



scala> :paste

// Entering paste mode (ctrl-D to finish)


import scala.concurrent.ExecutionContext.Implicits.global

import scala.concurrent._

import scala.concurrent.duration._


def fib(n: Int): Int = n match {

  case x if x < 0 =>

    throw new IllegalArgumentException(

      "Only positive numbers allowed")

  case 0 | 1 => 1

  case _ => fib(n-2) + fib(n-1)

}


object ValStore {

  lazy val fortyFive = fib(45)                   // (1)

  lazy val fortySix  = fib(46)                   // (2)

}


object Scenario1 {

  def run = {

    val result = Future.sequence(Seq(            // (3)

      Future {

        println(ValStore.fortyFive)

        println("done (45)")

      },

      Future {

        println(ValStore.fortySix)

        println("done (46)")

      }

    ))

    Await.result(result, 1.minute)

  }

}



// Exiting paste mode, now interpreting.


import scala.concurrent.ExecutionContext.Implicits.global

import scala.concurrent._

import scala.concurrent.duration._

fib: (n: Int)Int

defined object ValStore

defined object Scenario1


scala> Scenario1.run

1836311903

done (45)

-1323752223

done (46)

res4: Seq[Unit] = List((), ())




처음 실행할 때는 속도가 걸리지만, 다음 번 실행할 때는 무척 빠르다. lazy val의 특성이 있다. 


scala> Scenario1.run

-1323752223

done (46)

1836311903

done (45)

res5: Seq[Unit] = List((), ())


scala> Scenario1.run

1836311903

done (45)

-1323752223

done (46)

res6: Seq[Unit] = List((), ())






2번째 예제는 lazy val의 내부 synchronized를 이용해 deal lock을 유발시키는 코드이다. 여러 쓰레드를 사용하면서 lazy val을 잘 못 쓴다면 dead lock이 발생할 수 있다.



scala> :paste

// Entering paste mode (ctrl-D to finish)


import scala.concurrent.ExecutionContext.Implicits.global

import scala.concurrent._

import scala.concurrent.duration._


object A {

  lazy val base = 42

  lazy val start = B.step

}


object B {

  lazy val step = A.base

}


object Scenario2 {

  def run = {

    val result = Future.sequence(Seq(

      Future { A.start },                        // (1)

      Future { B.step }                          // (2)

    ))

    Await.result(result, 1.minute)

  }

}





dead lock이 발생했다.

scala> Scenario2.run
java.util.concurrent.TimeoutException: Futures timed out after [1 minute]
  at scala.concurrent.impl.Promise$DefaultPromise.ready(Promise.scala:255)
  at scala.concurrent.impl.Promise$DefaultPromise.result(Promise.scala:259)
  at scala.concurrent.Await$.$anonfun$result$1(package.scala:215)
  at scala.concurrent.BlockContext$DefaultBlockContext$.blockOn(BlockContext.scala:53)
  at scala.concurrent.Await$.result(package.scala:142)
  at Scenario2$.run(<console>:30)
  ... 29 elided






3번째 예제이다. 참조 블로그를 보면 deadlock 예제로 되어 있다. 


scala> :paste

// Entering paste mode (ctrl-D to finish)


import scala.concurrent.ExecutionContext.Implicits.global

import scala.concurrent._

import scala.concurrent.duration._


trait Compute {

  def compute: Future[Int] =

    Future(this.synchronized { 21 + 21 })        // (1)

}


object Scenario3 extends Compute {

  def run: Unit = {

    lazy val someVal: Int =

      Await.result(compute, 1.minute)            // (2)

    println(someVal)

  }

}



실제로 실행해 보면 deadlock은 발생되지 않는다. 



scala> Scenario3.run

42




lazy val에 synchronized가 된다면 인스턴스는 분명 deadlock 상황에 빠져야 한다. 그러나 컴파일러가 똑똑해져서 문제가 발생하지는 않는다.



 lazy val 다음 단계는 http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html에 있다.

Posted by '김용환'
,



docker container의 로그를 보려면 docker logs를 활용한다.


먼저 docker ps로 container id를 확인한다.


$ docker ps

CONTAINER ID        IMAGE                                                              COMMAND                  CREATED             STATUS              PORTS                      NAMES

3bfaccc746cc        dockerhub.google.com/excel/kami:develop   "/bin/sh -c 'target/u"   4 minutes ago       Up 4 minutes        0.0.0.0:10399->9000/tcp    kami-1


docker logs로 container log를 확인한다. 



$ docker logs 46817472b388

[info] application - Creating Pool for datasource 'default'

[info] p.a.d.DefaultDBApi - Database [default] connected at jdbc:mysql://1.1.1.1:3306/diction??autoReconnect=true&useSSL=false

[info] play.api.Play - Application started (Prod)

[info] p.c.s.AkkaHttpServer - Listening for HTTP on /0:0:0:0:0:0:0:0:9000



tail, 시간대역 별로도 볼 수 있다.


참조

https://docs.docker.com/engine/reference/commandline/logs/#usage




Posted by '김용환'
,