Siburlog

SiblogだとSI BlogっぽいのでSiburlogとする

Goで「配列のスライス」を「スライスのスライス」に変換する時は少し注意が必要

久しぶりにGoでハマった。下記のようなコードで。
The Go Playground

チェーン業界外の人のために注釈を付けておくと、本コードのcommon.Hashtype Hash [32]byteで定義されている配列です。
ループ変数(の一部)が配列である時に、これをスライシングするとループ変数を指すスライスが出来てしまい、期待とは違った動作になる、というお話。

じゃあなぜBytes()を使うと回避できるのかというと、こいつは値レシーバなのでループ変数のコピーをスライシングする形になるからです。
ただの便利メソッドじゃなかったのね...必ず使うようにします。


追記: ていうか、下記のやつの派生パターンに過ぎないのか。

gist.github.com

How to install yarn via pacman without node

Background

I decided to install NodeJS not using pacman but using a version manager (nvm at the moment).

However, according to Yarn official, I should install Yarn itself via an OS package manager for several reasons. https://yarnpkg.com/en/docs/install#alternatives-stable

At last, I have to install yarn via pacman skipping its only dependency, nodejs.

Solution

pacman -S --assume-installed nodejs yarn

Dell XPS13(9370)でSATA ModeをRAID OnからAHCIへ変更する時の注意

きっかけ

Dell XPS13(9370)を購入した。
Linuxで使うが、BIOS更新とかファーム更新のためにWindowsが必要になるかもなので、Dual Bootにすることにした。

Linuxをインストールして動かすには、UEFI設定画面に入って(POSTでF2連打)、SATA ModeをAHCIに変更する必要がある。
https://wiki.archlinux.org/index.php/Dell_XPS_13_(9370)#UEFI

が、これを単に実行したところ、Windowsが起動せず自動再起動するようになった。
とりあえずSATA ModeをRAID Onに戻して、Windowsが起動する状態には復旧できた。
しかし前述の通りSATA ModeはAHCIにしなきゃいけないし、Windowsも起動できないと困る。
どうすればいい?

対処

Windowsを一度セーフモードで起動すると、以降は問題なくなるらしい。
https://qiita.com/ak1t0/items/e7d574ae071143bbf10a

そこでまず、以下の手順に従ってセーフモードで起動するように設定してから再起動する。
https://support.microsoft.com/ja-jp/help/12376/windows-10-start-your-pc-in-safe-mode
(Win+R => msconfig => 「ブート」タブ => 「セーフブート」をオン)

UEFI設定画面に入り、SATA Modeを変更(RAID On -> AHCI)し、保存して抜ける。

Windows10がセーフモードで起動するので、セーフモードをオフに設定して、再起動する。

以降は問題なし!!!

追記

LVFSという制度があって、DellLinux向けのfirmware updateを供給してくれているようだ。

https://fwupd.org/

但し、ここに協力的なメーカーはまだ少数派のように見える。
例えば、今回のXPS13のSSDSamsung PM981だったのだが、Samsungはまだfirmwareをここにアップした実績は無いようだ。
というわけでWindows温存(Dual Boot)の方針が無難のようだ。

ALLOC_FAIRフラグについて

概要

Linuxカーネルのメモリページ割り当てに関するメモ。 ALLOC_FAIR というフラグについて説明する。

ページ割り当て処理

関数冒頭のコメントで This is the 'heart' of the zoned buddy allocator. と書かれている通り、ページ割り当て処理の中心は関数 __alloc_pages_nodemask() が担っている。 __alloc_pages_nodemask() の処理はざっくり言うと以下の通りである。

  1. First allocation attempt
    • 関数 get_page_from_freelist()でページ割り当てを試みる。 ALLOC_FAIR が指定されるのはこちら(ここだけ)。
  2. Slow path
    • First allocation attemptで失敗した場合、関数 __alloc_pages_slowpath() でページ割り当てを試みる。

First allocation attempt

First allocation attemptの際に、割り当て条件として指定されるフラグの一つが、 ALLOC_FAIR である。 First allocation attemptの処理をbreak downすると以下の通りである。

  1. ローカルノードでの割り当て試行
    • zonelist(説明割愛)で指定されたzone(説明割愛)の内、呼び出し元CPUと同じNUMAノード(ローカルノード)に属するzoneからの割り当てを試行する。
  2. 全ノードでの割り当て試行
    • zonelistで指定された全zoneについて、zonelistの登録順序で、再度割り当てを試行する。

このローカルノードでの割り当て試行を行う際、フラグ ALLOC_FAIR が指定されていると、1つのzoneがlow watermarkに達するまで消費してから次のzoneへ進む「のではなく」、各zoneにおいて一定量 x (= high_wmark_pages(zone) - low_wmark_pages(zone)) で表される量を割り当てたら、もう次のzoneへ進む、というロジックで割り当て試行処理が行われる。 ローカルノードに属するzone群でのページ消費負担を フェア に分散するロジックと理解できる。

他方、全ノードでの割り当て試行では、単純にzonelist順に一つのzoneがlow watermarkに達するまで消費して、次のzoneへ進む、というロジックとなる。

ちなみにローカルノードでの割り当て試行において、全てのzoneでの割当量が一旦 x に達すると、各zoneでいくら割り当てたかのカウンタがリセットされ、再び各zoneにおいて x までは割り当て可能となる。

JJUG CCC 2016 Fall感想

概要

JJUG CCC 2016 Fallに参加してきました。とても有意義で、幾つかのアクションの起点となりそう。 但し大遅刻で着いたのは昼、少し風邪気味で早退、今回聴講した講演は以下のみです。

  1. Spring超入門-Springを1年半使ってみて
  2. Java + spring-boot で書く! LINE BOT ライブコーディング。
  3. Spring CloudでDDD的なマイクロサービスを作ってみる
  4. JAX-RS REST Client で Cognitive Service や Excel を操作しよう
  5. Spring Cloudアプリケーションの開発にDockerを活用し、Kubernetes上にデプロイするまで

一つ一つ感想だけ書きます。内容は紹介しません(できない、とも言う)。

Spring超入門

Spring/Spring Bootについての入門的な内容でした。 短めのセッションなので、コードがっつりではなく概念・用語の簡単な紹介。 発表は分かりやすく、内容も盛り込み過ぎない感じで、気楽に聞けました。 Bootがっつり使ってるわけじゃないから、ところどころ知らない話もあって有意義でした。

Java + spring-boot で書く! LINE BOT ライブコーディング。

Spring Bootによってhello world的なLINE BOTを作る流れを紹介するライブコーディング。 BOT自体に特段の捻りはなかったと記憶しているけど、ライブコーディングはスムースでよく準備されてました(4回予行練習したとか)。 Spring BootでLINE BOT作るのが簡単だということが分かったし、自分でも作ろうと思ったので、個人的には刺激的で有意義でした。

Spring CloudでDDD的なマイクロサービスを作ってみる

DDDは仕事でもいつかガッツリやってやりたいと思いつつ、試みつつ、なかなか忙しさに流されてゴールできてない感じ。 そんなわけでこの発表聴いてみたけど、前半はDDDの基本概念を平易に説明する内容が中心で、分かりやすかったけど割と知ってる内容でもあり疲れてしまい、集中力が切れて、後半が頭に入りませんでした(弱い)。

JAX-RS REST Client で Cognitive Service や Excel を操作しよう

冒頭の、スピーカー寺田さんの、Cognitive Serviceは世界をより良くできるテクノロジー、というお話には考えさせられました。 そういう熱い思いを持ってる人がいるということと、一方で、ウチの仕事は世界を別に良くするものではないよなー、ってのと。 技術内容については、JAX-RS REST Clientというのがあるんだなー、REST叩く時は使ってみよう、Cognitive Serviceとか、ML系のクラウドサービス、色々組み合わせて遊びたいなー、などと思いました(小並感)。でもこれはこれでモチベ貰えて有意義でした。

Spring Cloudアプリケーションの開発にDockerを活用し、Kubernetes上にデプロイするまで

盛りだくさんの内容で、むしろ個々については詳しく理解できなかったんですが、Spring Cloudってライブラリの名前であってクラウド基盤ではないということが分かったのと(低レベルw でも使ってみようと思えたので良かった)、k8sは一度試してみようというモチベが得られて、有意義でした。

アクションアイテム

最後に、今回の聴講をきっかけとして、直近でやろうと思ったことを簡単に。

  • Spring Boot使ってLINE BOT作る
  • JAX-RS REST Client試す
  • Cognitive Service、とは限らないけど、ML系のクラウドサービス使って遊ぶ
  • Spring Cloud試す
  • Kubernetes試す

個別にやるのもいいけど、出来れば全部を魔合体して何か作りたいw 一石五鳥を目指して。

Golang覚書

概要

  • 最近Golang書いていて気付いた箇所・嵌った箇所や、自分の書き方の方針など。
  • 主旨はツッコミ待ち。会社で一人でGolang書いているので色々不安。

make(chan ...)でキャパを指定しないとunbufferedになる

  • キャパを指定しないと、適度なデフォルト値でもなく、1でもなく、0なので。
  • 同期処理用ということ。非同期プログラミングにおいては、むしろマイナー用途かも。

channelのバッファのキャパは送信側が管理すること

  • 溢れるかどうかは、受信側ではどうしようもないので、送信側がなんとかする。
  • 原則として、channelが溢れたらchannelを殺す(closeする)(しかない)。
  • channelのバッファはそのつもりで十分確保すること。
  • 送信側はchannel溢れを直ちに検知できるよう(間違ってもブロックなどせぬよう)、送信時は原則として以下のイディオムを使う。

      select {
      case chChild <- value:
      default:
          /* Kill the child */
      }
    

channelは原則として送信側が閉じること

  • closed channelに送信すると実行時例外が発生してしまうので、送信側でclosedか否かの管理をする。
  • 受信側では例外など出ずに自然にcloseをハンドリングできる。
  • 送信側だと、panic-recoverを用いた以下のイディオムでハンドリングは可能。もしどうしても必要なら。原則として避ける。

      func sendToUnstableChannel() {
          defer func() {
              recover()
          }
          ch <- v
      }
    

closeはブロックしない

  • cap=10のchanに10個のデータが既に詰まってる状況だと、更にsendしようとするとブロックするが、closeはブロックしない。
  • closeは非同期処理において安全(ブロックしない)処理だと言える。

channel受信は原則として、特別形式の受信式もしくはfor-range受信で行うこと

  • 以下のようにすると、chがcloseされた場合、無限busyループするので注意。

      Loop: for {
          select {
          case v := <-ch:
              /* Use a received value */
          case <-timeout:
              break Loop
          }
      }
    
  • 正しくは特別形式で受信

      for {
          select {
          case v, ok := <-ch:
              if !ok {
                  break Loop
              }
              /* Use a received value */
          case <-timeout:
              break Loop
          }
      }
    
  • もしくはfor-range受信(タイムアウトが設定できないのが難点)

      for v := range ch {
          /* Use a received value */
      }
    

ループの途中でコレクションの要素を削除する場合の注意

mapの場合は

  • delete文を使えば問題無い

      for k, v := range m {
          if cond(v) {
              delete(m, k)
          }
      }
    

sliceの場合は

  • downwardループを使う
    • append式での要素削除は、i以降の要素をshiftするため、i以降の要素に対してループを継続すると、おかしなことになる。
    • downwardなら問題は起こらない。

        for i := len(s) - 1; i >= 0; i-- {
            if cond(s[i]) {
                s = append(s[:i], s[i+1:]...)
            }
        }
      

原則としてbusyループは避けること

  • Goroutineは任意の箇所でプリエンプト出来る(される)わけではないので、下手をするとCPUを占有する可能性がある。
  • よほど理由があり問題無いと分かっているのでなければ、busyループは避ける。
  • ループがbusyループになりうるのではないか、気を付ける。