Zeals TECH BLOG

チャットボットでネットにおもてなし革命を起こす、チャットコマース『Zeals』を開発する株式会社Zealsの技術やエンジニア文化について発信します。

Trying to Build a Redux Implementation with Web Assembly in Rust

f:id:ssabcire:20200626105439p:plain

Introduction

Hi, I'm Aaron, a frontend engineer at Zeals. At Zeals, we use React for the frontend of our main application, with Redux as our primary method of managing state. While this works just fine for most of what we need our frontend to do, I like to explore other options and how they compare to established solutions.
So today, I thought it would be interesting trying to try to port Redux to web-assembly, and see how this compares to its Javascript counterpart. And when I say "compare", I mean this in the loosest sense.
I'm not familiar on all the latest web-assembly news and technology, so this is simply an opportunity to explore it and share what it was like.

This article somewhat assumes the reader is somewhat familiar with React. I will try not to spend too much time on it as it could be an entire article on its own, but not knowing react should not be a hurdle to understanding the larger implications.
I will spend some time talking about Redux/Rust/Wasm, as it will guide the reader into understanding how I tried to use Wasm, and perhaps why the trade-offs work the way they do.


  • Introduction
    • Web-assembly
    • Rust/Wasm
    • Goal
    • On Redux
  • The code
    • Introduction
    • Generating the Rust/Wasm Library
    • The React-JS project
    • Using the library in the new JS Project
    • Creating the Redux Store
    • The Redux "Store" methods
    • Using the redust "library" in our JS code
  • Result
    • Speed test
    • Why?
  • Conclusion
続きを読む

新卒から見たZealsエンジニア組織について

f:id:dainiizawa:20200316104115p:plain


はじめまして!現在Zealsで内定者インターンをしている玉城です!
私は大学の在学期間中、GoとGCPを使ったWebアプリケーションの作成や時間短縮ツールなどを独学で作成したり、自然言語処理と機械学習を用いた研究を行っていました。Zealsには、利用している技術や提供しているサービス、熱量の高さから、自分がやりたいことを達成できる環境だと思い入社することを決めました。
現在、内定者インターンという形でジョインしてからおよそ3ヶ月ほど経ちましたが、とても凄い環境で圧倒されています。
そこで今回は、新卒から見たZealsの開発組織と環境について書いていきます。
特に、

  • 私がZealsでどのように働いているのか
  • エンジニアが働きやすい環境か
  • 新卒でも主体的に動ける環境があるか

こういった問いを持つ方に、少しでも参考になれば幸いです。

続きを読む

GKE環境のRedashでPostgreSQLサーバのディスクが溢れたので、Persistent Volumeをいじって何とかした

f:id:lead-zep0324teruhisa:20200202211533p:plain

こんにちは、分析基盤を担当している鍵本です。

本日は昨年の10月に GKE に移行した Redash の PostgreSQL サーバでディスクがいっぱいになってサービス停止してしまった 時のお話をします。

GKE に移行した話については2019年11月15日に公開したテックブログ Redash 分析環境のGKE移設&ver.3から7へのアップデート手順を公開 を御覧ください。

tech.zeals.co.jp

続きを読む

fast_jsonapi gemを1.2から1.5へアップデートしたら躓いた点と対応策

f:id:zeals-engineer:20200124000233p:plain

こんにちは、分析基盤を担当しつつ Rails エンジニアのサポートもやっている鍵本です。

本日は Fast JSON API のバージョンアップをした時に突然テストが落ちて困った話をします。

github.com

  • 背景
  • テストが落ちる状態の再現
  • エラーの原因
  • 対応方法
  • まとめ
  • 最後に

背景

Zealsは、LINEおよびFacebook Messenger上で動作するチャットボットサービスで、WebのForm体験をチャット上で完結させることができます。この Zeals のアーキテクチャは同じくテックブログの記事『進化し続ける「Zeals」サービスを支えるアーキテクチャについて』でご紹介しているとおり、バックエンドを Ruby on Rails と React + Redux (& TypeScript) で実装しています。

tech.zeals.co.jp

Rails は DB から必要なデータを準備し JSON 形式に変換したものをフロント側の React に渡しています。その JSON 形式に変換する部分に Fast JSON API を用いています。アップデート前はバージョン 1.2 を用いておりテストコードも問題なく通っていたのですが、1.5 に gem のアップデートをしたところ突然テストが通らなくなりました。

テストが落ちる状態の再現

サンプルプログラムを使ってテストが落ちた状態を再現してみます。

属性として size, color を持つ Shirt モデルを作成します。

class Shirt < ApplicationRecord
  enum size: { 'XS': 0, 'S': 1, 'M': 2, 'L': 3, 'XL': 4 }
end

Shirt モデルのためのシリアライザを以下のように作成します。

class ShirtSerializer < ActiveModel::Serializer
  include FastJsonapi::ObjectSerializer

  attributes :size, :color
end

Shirt モデルをシリアライズして JSON を返すコントローラを作成します。

class ShirtController < ApplicationController
  def show
    @shirt = Shirt.find(params[:id])

    shirt_json = ShirtSerializer.new(@shirt).serializable_hash[:data][:attributes]

    render json: shirt_json
  end
end

ShirtController#show は {size: サイズ, color: 色} を返すので、例えば size: 'M' となる Shirt を生成して、その値が正しく得られることをテストするコードを用意します。

require 'rails_helper'

RSpec.describe ShirtController, type: :controller do
  describe '#show' do
    let(:shirt) { create(:shirt, size: 'M', color: 'black') }

    subject { JSON.parse(response.body, symbolize_names: true) }

    example 'response の size が M である' do
      get :show, params: { id: shirt.id }

      expect(subject[:size]).to eq shirt.size
    end
  end
end

fast_jsonapi のバージョンを 1.2 としてテストすると問題なく成功します。

$ bundle exec rspec spec/controllers/shirt_controller_spec.rb
.

Finished in 0.02659 seconds (files took 1.39 seconds to load)
1 example, 0 failures

ここで fast_jsonapi のバージョンを 1.5 にアップデートすると以下のような結果を得ます。

$ bundle exec rspec spec/controllers/shirt_controller_spec.rb
F

Failures:

  1) ShirtController#show response の size が M である
     Failure/Error: shirt_json = ShirtSerializer.new(@shirt).serializable_hash[:data][:attributes]

     NoMethodError:
       undefined method `each' for #<Shirt:0x00007f9478a3f318>
     # ./app/controllers/shirt_controller.rb:5:in `show'
     # ./spec/controllers/shirt_controller_spec.rb:10:in `block (3 levels) in <top (required)>'

Finished in 0.01942 seconds (files took 1.43 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/controllers/shirt_controller_spec.rb:9 # ShirtController#show response の size が M である

エラーの原因

fast_jsonapi の serializable_hash メソッドは ActiveRecord::Relation オブジェクト か否かで処理を分けているのですが、その判定方法が 1.2 以前とそれ以降で変わってしまったことが原因でした。具体的には 1.3 以降では以下のような条件で判定しています。

      resource.respond_to?(:size) && !resource.respond_to?(:each_pair)

我々の Shirt モデルには enum で size という属性を持っているため size メソッドが存在します。そのため ActiveRecord::Relation オブジェクトであると間違って判定され、

@shirt.each do |s|
  ...
end

のような処理が実行され、each メソッドが存在しない というエラーが出たということでした。因みに 1.2 以前では以下のような判定だったので問題なかったというわけです。

      resource.respond_to?(:each) && !resource.respond_to?(:each_pair)

対応方法

テーブルスキーマを変更して列名を size から別のものに変更すればいいのですが、影響範囲が大きいためシリアライズする際にオプションを渡すことで対応することとしました。すなわち、

class ShirtController < ApplicationController
  def show
    @shirt = Shirt.find(params[:id])
    options = { is_collection: false }

    shirt_json = ShirtSerializer.new(@shirt, options).serializable_hash[:data][:attributes]

    render json: shirt_json
  end
end

@shirt が ActiveRecord::Relation オブジェクトでないことがわかっているから採用できる方法です。これで先のテストコードが成功することがわかります。

続きを読む

TypeScriptでconditionベースのサブセットタイプを作成する

f:id:zeals-engineer:20191229220455p:plain Photo by Alvaro Reyes on Unsplash

究極の謎を解くためのタイピングシステムの詳細について

こちらの記事は翻訳記事となります。 原著者の許諾を得て翻訳・公開しております。

medium.com

続きを読む

会話分析におけるGraphDBの活用

f:id:newton30000:20191215174040p:plain

こんにちは。ZealsでCTOをしている佐藤です。
Zealsでは AdventCalendarを開催しており、そちらの15日目の記事となります!

  • はじめに
    • 会話分析とは?
    • 何がしたいか
  • Neo4j
    • 技術選定について
    • 環境構築・実行
  • 振り返り
  • さいごに

はじめに

会話分析とは?

私達が開発しているZealsは「ネットにおもてなし革命を!」をコンセプトにした、ユーザーと会話して商品を案内するチャットコマースのサービス です。

チャットコマースについて詳細が知りたい方は、同じくZeals AdventCalenarで公開された以下の記事をご覧ください!

qiita.com

このZealsの会話体験を向上させていく中で、ユーザーとチャットボットとの 会話データ を分析していく必要があります。

現在ではMySQLやBigQueryにデータを格納することで、 ETL分析や機械学習のためのマスターデータ を用意しています。 (機会があればこのテーマも記事にします)

www.ashisuto.co.jp

何がしたいか

f:id:newton30000:20191215173727p:plain

例えば会話構造の一部をサンキーチャートで表すと、上記のように表示されます。

ja.wikipedia.org

KPIの取得や特徴量の把握に関しては運用上の問題は特に発生していないのですが、機能が拡張する中で分析しきれない(あるいはしづらい)領域が出てきてしまいます。

例えば上記のサンキーチャートですが、再帰的な構造の会話には対応することができません。

  • ユーザー毎の会話の分岐を構造化
  • 会話が再帰的な構造に対するクエリ発行
  • ユーザーと会話の関係を表すデータが多様
  • 将来的にテーブルが肥大化することによって性能が悪化する

このような問題に対応するため、 RDB以外でデータ格納や分析を行うアプローチ を検討しました。

続きを読む