デフォルトゲートウェイが複数存在してしまいWi-Fiからインターネットに接続できない問題の解決

以下のような構成にした時、デフォルトゲートウェイがロボット側のネットワークのものを指してインターネットに接続できなくなってしまう問題が起こりました。

f:id:asmsuechan:20180720175318p:plain

解決手順

  1. イーサネットケーブルを抜いてWi-Fi接続の状態でrouteを実行してデフォルトゲートウェイIPアドレスを調べる
  2. イーサネットケーブルを挿す
  3. sudo route add default gw <1で調べたIPアドレス>を実行する
  4. routeコマンドでロボット側のデフォルトゲートウェイIPアドレス(192.168.xx.xx)を調べる
  5. sudo route del default gw <4で調べたIPアドレス>を実行する
asmsuechan@asmsuechan-dynabook-R93-PB:~$ route # イーサネットケーブルを抜いてWi-Fi接続した状態で
カーネルIP経路テーブル
受信先サイト    ゲートウェイ    ネットマスク   フラグ Metric Ref 使用数 インタフェース
default         172.16.199.xxx  0.0.0.0         UG    0      0        0 wlan0
172.16.192.0    *               255.255.248.0   U     9      0        0 wlan0
asmsuechan@asmsuechan-dynabook-R93-PB:~$ # イーサネットケーブルを挿す
asmsuechan@asmsuechan-dynabook-R93-PB:~$ sudo route add default gw 172.16.199.xxx 
asmsuechan@asmsuechan-dynabook-R93-PB:~$ route
カーネルIP経路テーブル
受信先サイト    ゲートウェイ    ネットマスク   フラグ Metric Ref 使用数 インタフェース
default         172.16.199.xxx  0.0.0.0         UG    0      0        0 wlan0
default         192.168.0.1     0.0.0.0         UG    0      0        0 eth0
172.16.192.0    *               255.255.248.0   U     9      0        0 wlan0
192.168.0.0     *               255.255.255.0   U     1      0        0 eth0
asmsuechan@asmsuechan-dynabook-R93-PB:~$ sudo route del default gw 192.168.0.1

routeで表示される受信先サイトの列がdefaultとなっている行がデフォルトゲートウェイです。

それぞれに優先度を与えて複数のデフォルトゲートウェイを設定することもできますが、普通デフォルトゲートウェイは1つなので綺麗にその構成にした方がいざという時に困らないと思います。

参考

デフォルトゲートウェイを理解する 基礎からのネットワーク⑤ – ランスルネット

【Linux】 NIC(インターフェース)が複数ある場合の設定方法 | 100%レンタルサーバーを使いこなすサイト

aliyun-log-cliの使い方

AlibabaCloudのLog ServiceをCLIから操作するツールであるaliyun-log-cliを使ってみます。

aliyunlogの設定

ドキュメントを参考にaliyunlogを設定します。

jp.alibabacloud.com

なお、ドキュメントのエンドポイントは間違っています(2018/07/20)。エンドポイントは以下を参考にしてください。

jp.alibabacloud.com

$  aliyunlog configure
Invalid parameters.

Usage:
aliyun configure <secure_id> <secure_key> <endpoint> [<client_name>]
aliyun configure [--format-output=json] [--default-client=<client_name>] [--decode-output=utf8,latin1]
$ aliyunlog configure LTAxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx cn-shanghai.log.aliyuncs.com

設定した情報は~/.aliyunlogcliに保存されます。

プロジェクトの作成

create_projectでプロジェクトを作成します。

$ aliyunlog log create_project --project_name="my-project" --project_des="my project"

descriptionは必須なんですね。

my-projectという名前のプロジェクトが作成されたことをget_projectで確認します。

$ aliyunlog log get_project --project_name="my-project"
{"createTime": "2018-07-20 12:33:38", "description": "my project", "lastModifyTime": "2018-07-20 12:33:38", "owner": "5326127221743842", "projectName": "my-project", "region": "cn-shanghai", "status": "Normal"}

logstoreの作成

create_logstoreを使ってlogstoreを作成します。

$ aliyunlog log create_logstore --project_name="my-project" --logstore_name="my-logstore"

my-project内にmy-logstoreが作成されたことをget_logstoreで確認します。

$ aliyunlog log get_logstore --project_name="my-project" --logstore_name="my-logstore"
{"autoSplit": false, "createTime": 1532061300, "enable_tracking": false, "lastModifyTime": 1532061300, "logstoreName": "my-logstore", "maxSplitShard": 0, "shardCount": 2, "ttl": 30}

API Gatewayと紐付ける

まずAPI Gatewayを作成します。以下が参考になります。

asmsuechan.hatenablog.com

画面からポチポチしてLogstoreとAPI Gatewayを紐付けます。「データ・インポート・ウイザード」をクリックします。

f:id:asmsuechan:20180720134754p:plain

次の画面のクラウドサービスでAPI Gatewayを選択してRAMを適切に設定します。

ログ使用モードの「検索」で見るとちゃんとアクセスログが送信されていることが確認できました。

f:id:asmsuechan:20180720135117p:plain

ログの取得

aliyunlogを使ってログを取得します。

$ aliyunlog log get_logs --request="{\"topic\": \"\", \"logstore\": \"my-log-store\", \"project\": \"my-logservice\", \"toTime\": \"2018-07-21 13:10:10\", \"offset\": \"0\", \"query\": \"*\", \"line\": \"10\", \"fromTime\": \"2018-07-17 08:08:08\", \"reverse\":\"true\"}"
[{"__source__": "log_service", "__time__": "1532060533", "__topic__": "", "apiGroupName": "HttpFunctionGroup", "apiGroupUid": "bee62351bf0446e4adb717424a54a4f3", "apiName": "hello_get", "apiStageName": "RELEASE", "apiStageUid": "040f58293163437e806a23891dc74b48", "apiUid": "c4bb4b9233d241bead52df248ad02993", "appId": "", "appName": "", "clientIp": "133.26.40.24", "domain": "bee62351bf0446e4adb717424a54a4f3-cn-shanghai.alicloudapi.com", "errorMessage": "OK", "exception": "", "httpMethod": "GET", "path": "/", "providerAliUid": "5326127221743842", "region": "cn-shanghai", "requestHandleTime": "2018-07-20T04:22:11Z", "requestId": "03D8528D-A294-4919-AC36-A889688FBECA", "requestSize": "284", "responseSize": "13", "serviceLatency": "85", "statusCode": "200"}]

取得できました。

まとめ

Function ComputeとLogServiceを連携することでサーバレスなシステムを運用しやすくなります。

aliyun-log-cliのインストールができない

Alibaba CloudのLog SserviceのCLIツールであるaliyun-log-cliというものがあります。

User Guide — Aliyun Log Service CLI 0.1.13 documentation

しかしaliyun-log-cliを使おうと公式ドキュメントのインストールガイドに従ったところ以下のエラーが出ました。

$ ./aliyunlog
Traceback (most recent call last):
  File "./aliyunlog", line 9, in <module>
    load_entry_point('aliyun-log-cli', 'console_scripts', 'aliyunlog')()
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pkg_resources/__init__.py", line 565, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2631, in load_entry_point
    return ep.load()
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2290, in load
    self.require(*args, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2307, in require
    items = working_set.resolve(reqs, env, installer)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pkg_resources/__init__.py", line 853, in resolve
    raise DistributionNotFound(req, requirers)
pkg_resources.DistributionNotFound: The 'jmespath' distribution was not found and is required by the application

メッセージに出ているjmespathは既にインストールされていました。

$ pip3 install -U jmespath
Requirement already up-to-date: jmespath in /usr/local/lib/python3.6/site-packages (0.9.3)

もしやと思ってaliyun-log-cliの場所を見てみました。

$ pip3 show aliyun-log-cli
Name: aliyun-log-cli
Version: 0.1.13
Summary: Aliyun log service CLI
Home-page: https://github.com/aliyun/aliyun-log-cli
Author: Aliyun
Author-email: UNKNOWN
License: UNKNOWN
Location: /Users/asmsuechan/Library/Python/3.6/lib/python/site-packages
Requires: aliyun-log-python-sdk, jmespath, docopt
Required-by:

これが原因っぽいですね。

python3のパッケージの参照先が/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packagesになっているのに対し、pip3はパッケージを/Users/asmsuechan/Library/Python/3.6/lib/python/site-packagesにインストールしています。

というわけでpython3の参照先を変更したかったのですが、brewでインストールしたPythonがいたりサーチパスがappendされなかったりと自分の環境がまずい上にAttributeError: module 'enum' has no attribute 'IntFlag'など他のエラーも出てどうにもうまくいかなかったので、Pythonの環境をanacondaで構築し直しました

www.anaconda.com

anacondaのpython3でやってみる

まずはanacondaのpipでaliyun-log-cliをインストールします。

$ ~/anaconda3/bin/pip install -U aliyun-log-cli
Collecting aliyun-log-cli
(中略)
Installing collected packages: jmespath, protobuf, enum34, aliyun-log-python-sdk, docopt, aliyun-log-cli
Successfully installed aliyun-log-cli-0.1.13 aliyun-log-python-sdk-0.6.30.4 docopt-0.6.2 enum34-1.1.6 jmespath-0.9.3 protobuf-3.6.0

パッケージは/Users/asmsuechan/anaconda3/lib/python3.6/site-packagesに保存されます。

ではドキュメントを参考にaliyunlog.pyを以下のように作成します。(ただ冒頭のShebangを消して拡張子を.pyにしただけのものです)

import re
import sys
from pkg_resources import load_entry_point

if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
    sys.exit(
        load_entry_point('aliyun-log-cli', 'console_scripts', 'aliyunlog')()
    )

さてこれをanacondaのpython3を使って実行します。

$ ~/anaconda3/bin/python3 aliyunlog.py

Usage:

1. aliyunlog log <subcommand> [parameters | global options]
2. aliyunlog configure <access_id> <access-key> <endpoint> [<client-name>]
3. aliyunlog configure [--format-output=json] [--default-client=<client_name>] [--decode-output=utf8,latin1]
4. aliyunlog [--help | --version]


Examples:

1. aliyunlog configure AKID123 AKKEY123 cn-hangzhou.log.aliyuncs.com
2. aliyunlog configure --format-output=json --default-client=beijing
3. aliyunlog log create_project --project_name="test"
...()

できました。

以上、Alibaba Cloudほとんど関係ないPythonの問題でした。

まとめ

なんの根本解決もしていませんが、何か起きる度に手でpipとpythonの整合性を合わせ続けるのは限界なのでanacondaを使いました。

ちなみにこういう時こそDockerが便利だと思ってやってみましたがDocker力(Python力かも)が足らずに失敗しています。。。悲しい。。。

$ alias python='docker run -it -v `pwd`/:/src -w /src -e PYTHONPATH="/src/.pip" python python'
$ alias 'pip-install'='docker run -it -v `pwd`/:/src -w /src python pip install --target=/src/.pip'
$ pip-install aliyun-log-cli
$ python aliyunlog.py
Traceback (most recent call last):
  File "aliyunlog.py", line 1, in <module>
    import re
  File "/usr/local/lib/python3.6/re.py", line 142, in <module>
    class RegexFlag(enum.IntFlag):
AttributeError: module 'enum' has no attribute 'IntFlag'

Function ComputeのログをLogServiceに吐き出す

やること

Function ComputeのログをLog Serviceに出すようにします。

公式ドキュメント(「FC と LogService」)を参考にしましたが、ちょっと情報が古い気がするので自分なりに少し読み替えました。

jp.alibabacloud.com

Function ComputeとLog Serviceの設定

まず、Function Computeと同じリージョンのLog ServiceのプロジェクトとLogStoreを作成しましょう。

f:id:asmsuechan:20180718125654p:plain

次に、ログを出したいFunction Computeのサービスの画面から「詳細設定」->「編集」よりログプロジェクトを設定します。

f:id:asmsuechan:20180718125843p:plain

先ほど作ったLog ServiceのLogStoreを選択してOKを押します。

ログを確認する

さて、FunctionComputeの関数で適当にログを吐くようにします。

module.exports.handler = function(request, response, context) {
  console.log('handler is called!')
  (中略)
}

ここで作った関数を実行すると、作ったLogstoreLogHubにログが保存されます。

f:id:asmsuechan:20180718132830p:plain

ちゃんとログが吐かれるようになりました。

さらに、CloudMonitorでモニタリングもできます。Logstoreの「モニター」をクリックしたらCloudMonitorでジャンプします。

f:id:asmsuechan:20180718134606p:plain

まとめ

ドキュメントの情報が少し古いのは、活発に開発されている証拠ですね。

Function ComputeとAPI Gatewayのデプロイツールであるfunを使う

Alibaba CloudのFunction ComputeとAPI Gatewayを使ってサーバレスAPIを構築するときに便利なfcuというツールを使います。

公式ドキュメントを参考にしますが、いまいちよく分からない部分がありますので(2018/07/16)、合わせてGitHubリポジトリも参考にします。

fun - Developer Guide| Alibaba Cloud ドキュメントセンター

github.com

準備

まず、fcuをインストールします。

$ npm install @alicloud/fun -g
or
$ yarn global add @alicloud/fun

設定ファイルの作成

設定ファイルを書きます。公式ドキュメントのfaas.yamlは間違っていると思うので、GitHubの方を参照します。

GitHubの方にはtemplate.ymlを作れと書かれているのでこちらに従います。

$ mkdir fun
$ cd fun
$ touch template.yml

以下のtemplate.ymlGitHubのサンプルほとんどそのままです。

# template.yml
ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
  fc: # service name
    Type: 'Aliyun::Serverless::Service'
    Properties:
      Description: 'fc test'
    httpFunction: # function name
      Type: 'Aliyun::Serverless::Function'
      Properties:
        Handler: httpFunction.handler
        Runtime: nodejs8
        CodeUri: './'
        Timeout: 60

  HttpFunctionGroup: # Api Group
    Type: 'Aliyun::Serverless::Api'
    Properties:
      StageName: RELEASE
      DefinitionBody:
        '/': # request path
          get: # http method
            x-aliyun-apigateway-api-name: hello_get # api name
            x-aliyun-apigateway-fc:
              arn: acs:fc:::services/${fc.Arn}/functions/${httpFunction.Arn}/

コードを書く

次に、APIで実行されるコードを書きます。

// httpFunction.js
const { hook } = require('fc-helper')

exports.handler = hook(async (context) => {
  context.body = 'hello world!\n'
})

なお、hook関数の引数に入れる関数はasyncであることが必須です。

fc-helperはnpm管理のライブラリなのでpackage.jsonを作成して追加します。

$ yarn init
$ yarn add fc-helper

デプロイする

ではデプロイします。node_ modules/以下も勝手に固めて送ってくれるみたいで便利です。

$ fun deploy
Waiting for service fc to be deployed...
    Waiting for function httpFunction to be deployed...
    function httpFunction deploy success
service fc deploy success

Waiting for api gateway HttpFunctionGroup to be deployed...
    URL: GET http://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-cn-shanghai.alicloudapi.com/
      stage: RELEASE, deployed, version: 20180716123043059
      stage: PRE, undeployed
      stage: TEST, undeployed
api gateway HttpFunctionGroup deploy success

実際にリクエストを送って動作を確認するにはAPI Gatewayのコンソール画面にある「APIデバッグ」から行います。 f:id:asmsuechan:20180716132937p:plain

エラーがあったらエラーメッセージが、成功していたら200のレスポンスが返ってきます。

curlでも同じものが確認できます。

$ curl http://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-cn-shanghai.alicloudapi.com/
HTTP/1.1 200 OK
Server: Tengine
Date: Mon, 16 Jul 2018 04:46:17 GMT
Content-Type: text/plain
Content-Length: 13
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH
Access-Control-Allow-Headers: X-Requested-With,X-Sequence,X-Ca-Key,X-Ca-Secret,X-Ca-Version,X-Ca-Timestamp,X-Ca-Nonce,X-Ca-API-Key,X-Ca-Stage,X-Ca-Client-DeviceId,X-Ca-Client-AppId,X-Ca-Signature,X-Ca-Signature-Headers,X-Ca-Signature-Method,X-Forwarded-For,X-Ca-Date,X-Ca-Request-Mode,Authorization,Content-Type,Accept,Accept-Ranges,Cache-Control,Range,Content-MD5
Access-Control-Max-Age: 172800
X-Ca-Request-Id: 9D0447A1-B5BA-43F9-BFCA-A305AC931844

hello world!

出たエラー

1: 公式ドキュメントに従ってfaas.yamlを作成した時のエラー。GitHubの方を参考にしてtemplate.ymlを作成しましょう。

$ fun deploy
Error: ENOENT: no such file or directory, open '/Users/asmsuechan/src/alicloud_practice/function_compute/fun/template.yml'

2: fun deployで以下のエラーが出る場合はAPI Gatewayが有効化されていません。有効化しましょう。

$ fun deploy
Waiting for service fc to be deployed...
    Waiting for function httpFunction to be deployed...
    function httpFunction deploy success
service fc deploy success

Waiting for api gateway HttpFunctionGroup to be deployed...
ServiceUnOpenError: Your Gateway service has not been opened.
    at httpx.request.then.then (/Users/asmsuechan/.config/yarn/global/node_modules/@alicloud/pop-core/lib/core.js:169:19)
    at process._tickCallback (internal/process/next_tick.js:68:7)

まとめ

公式ドキュメントに書かれている内容が違うのはバージョンが古いからなのでしょうか。といっても最終更新日は2ヶ月前なのですけど。

funコマンドのおかげでGitHubのマスターブランチにマージされた時などにCIから簡単にデプロイが行えるようになります。嬉しいですね。

TableStoreで自動インクリメントする

Alibaba CloudのTableStoreのAUTO_INCREMENTオプションを使って自動インクリメントするようにします。

nodejsで自動インクリメントするプライマリキーを持ったテーブルを作成します。

const TableStore = require('tablestore')

const instanceName = 'teststorage'
const tableName = 'test'

const client = new TableStore.Client({
  accessKeyId: "xxxxxxxxxxxxxxxxxxxxx",
  secretAccessKey: "xxxxxxxxxxxxxxxxxxxxxxxxxx",
  endpoint: `https://${instanceName}.cn-hangzhou.ots.aliyuncs.com`,
  instancename: instanceName,
})

const params = {
  tableMeta: {
    tableName: 'autoIncTable',
    primaryKey: [
      {
        name: 'integerPK',
        type: 'INTEGER'
      },
      {
        name: 'autoIncPK',
        type: 'INTEGER',
        option: 'AUTO_INCREMENT' // ここ大事!
      },
    ]
  },
  reservedThroughput: {
    capacityUnit: {
      read: 0,
      write: 0
    }
  },
  tableOptions: {
    timeToLive: -1,
    maxVersions: 1
  }
}

client.createTable(params, (err, data) => {
  if (err) {
    console.log('error:', err)
    return
  }
  console.log('success:', data)
})

自動インクリメントするためにはINTEGERのプライマリキーにオプションとしてoption: ‘AUTO_INCREMENT’を設定する必要があります。

$  node table_store/createTable.js
success: { RequestId: '00000000-0000-0000-0000-000000000000' }

公式ドキュメントは以下です。 jp.alibabacloud.com

公式のサンプルコードのここにAUTO_INCREMENTの指定が書かれています。 aliyun-tablestore-nodejs-sdk/primarykey.js at 8a9f0ab8c520874d3177afe97c193776dca80747 · aliyun/aliyun-tablestore-nodejs-sdk · GitHub

挿入する

さて、では実際に値を挿入してみましょう。まずnodejsのコードから値を挿入します。

const TableStore = require('tablestore')
const Long = TableStore.Long;
const instanceName = 'teststorage'

const client = new TableStore.Client({
  accessKeyId: "xxxxxxxxxxxxxxxxxxxxxxx",
  secretAccessKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  endpoint: `https://${instanceName}.cn-hangzhou.ots.aliyuncs.com`,
  instancename: instanceName,
})

const params = {
  tableName: 'autoIncTable2',
  condition: new TableStore.Condition(TableStore.RowExistenceExpectation.IGNORE, null),
  primaryKey: [{ 'integerPK': Long.fromNumber(3) }, { 'autoIncPK': TableStore.PK_AUTO_INCR }],
  attributeColumns: [
    { 'www': Long.fromNumber(3) },
  ],
  returnContent: { returnType: TableStore.ReturnType.Primarykey }
}

client.putRow(params, (err, data) => {
  if (err) {
    console.log('error:', err)
    return
  }
  console.log('success:', data)
})

自動インクリメントするキーの値にTableStore.PK_AUTO_INCRを指定すればあとは勝手にやってくれます。

$ node createRow.js
success: { consumed: { capacity_unit: { read: 0, write: 1 } },
  row: { primaryKey: [ [Object], [Object] ], attributes: [] },
  RequestId: '000570ef-9c43-79d4-5576-d80b5c87e550' }

ではコンソールからテーブルデータを確認します。

f:id:asmsuechan:20180714154149p:plain

おや、自動インクリメントされるキーに1531549702197000という謎の数値が入っています。

なんとなくDateっぽいので調べてみます。

> new Date(1531549702197)
Sat Jul 14 2018 15:28:22 GMT+0900 (Japan Standard Time)

当たり。1000倍させられたDateさんでした。なんで1000倍されてるんだ。

なお、ドキュメントによるとこの値の型は64ビット符号長整数型だそうです。

まとめ

上の通り、TableStoreでは一般的なプログラミングでのインクリメントはされません。

ただただユニークで格納順に大きくなる値が入るだけです。

誤訳かと思うのですが、オプション名がAUTO_INCREMENTなんですよね・・・

TableStoreでテーブル内の全てのオブジェクトを取得する方法

プライマリキーに対してgetRange()メソッドを実行します。この時プライマリキーの範囲をTableStore.INF_MIN以上TableStore.INF_MAXにすることで全てのオブジェクトを取得することができます。

実はドキュメント通り。

複数行操作 - 開発ガイド| Alibaba Cloud ドキュメントセンター

const TableStore = require('tablestore')
const Long = TableStore.Long
const instanceName = 'teststorage'

const client = new TableStore.Client({
  accessKeyId: "xxxxxxxxxxxxxx",
  secretAccessKey: "xxxxxxxxxxxxxxxxxxxxxxxxx",
  endpoint: `https://${instanceName}.cn-hangzhou.ots.aliyuncs.com`,
  instancename: instanceName,
})

const params = {
  tableName: "locations",
  direction: TableStore.Direction.FORWARD,
  inclusiveStartPrimaryKey: [{ "uuid": TableStore.INF_MIN }],
  exclusiveEndPrimaryKey: [{ "uuid": TableStore.INF_MAX }],
  limit: 50
}

client.getRange(params, function (err, data) {
  if (err) {
    console.log('error:', err)
    return
  }
  console.log('success:', data)
  console.log(data.rows[0])
})

data.rowsに全てのオブジェクトが入っています。

$ node getRange.js
success: { consumed: { capacity_unit: { read: 1, write: 0 } },
  rows:
   [ { primaryKey: [Array], attributes: [Array] },
     { primaryKey: [Array], attributes: [Array] },
     { primaryKey: [Array], attributes: [Array] } ],
  next_start_primary_key: null,
  next_token: null,
  RequestId: '000570e0-97c0-f4e2-697d-da0b4a4e7184' }

{ primaryKey: [ { name: 'uuid', value: '5591f4f4f7088729' } ],
  attributes:
   [ { columnName: 'created_at',
       columnValue: 1531287325266,
       timestamp: [Int64] },
     { columnName: 'mesh_code',
       columnValue: 52354000,
       timestamp: [Int64] } ] }