「session deleted because of page crash」エラーの解決方法

Rspecでテストを実行すると、以下のエラーが発生しました。

1
2
3
4
RSpec::Core::MultipleExceptionError: unknown error: session deleted because of page crash
from unknown error: cannot determine loading status
from tab crashed
(Session info: headless chrome=117.0.5938.62)

解決策

Chromeブラウザを最新化すると解決しました。

【RubyMine】「SDKにRailsが見つかりません」エラーの解決方法

解決策

SDKを追加する(そのままですね)

  • RubyMine>設定>言語 &フレームワーク>zaico_webで「+」ボタンを押す

【読書メモ】Webパフォーマンスチューニング

Chapter1 チューニングの基礎知識

  • 推測するな、計測せよ
  • ボトルネックはどこか?CPU?メモリ?
  • ネットワークIO?アプリ処理?データベースIO?など

Chapter2 モニタリング

  • CPUやメモリの監視→モニタリング
  • totalコマンドやfree --humanコマンドでそれぞれの情報を確認できる
  • が、それだとその瞬間の値しか見れない
  • 推移が見たいときは例えば1分ごとに実行し、結果を記録する必要がある
  • 手動じゃ無理
  • なのでツールに頼ろう
  • 有名なのはPrometheus(プロメテウス)
  • これは監視用のサーバーを別途立てて、そこにPrometheusを起動して、監視対象のサーバーに定期的にアクセスして結果を記録するやつ
  • 監視対象先のサーバーにもPrometheusからのアクセスされるためのエンドポイントの用意が必要
  • これはnode_exporterツールを使うと便利
  • Prometheusはモニタリング結果のダッシュボード表示もできるよ
  • ハマりがちな罠:CPUが複数ある場合、瞬間的な増減の見落としなど

Chapter3

  • 負荷試験をやろう
     - abコマンド(Apache Bench)が便利
     - 指定したURLに指定した回数擬似リクエストできる
  • topコマンドで、MySQL・Webアプリ・WebサーバーなどどれがCPUを多く使っているか確認しよう
  • MySQLがボトルネックの場合、遅いSQL(スロークエリ)を特定しよう
     - my.cnfのslow_query_logを1に設定すると表示される
  • 対象のSQLが特定できたらEXPLAINで実行計画を確認しよう
  • 必要に応じてインデックスを貼るなど検討しよう

Chapter4

  • 単一のURLへの負荷試験に加えて、シナリオを持った負荷試験をやろう
  • 例えばログインして、データを登録して、削除して、とか
  • k6というツールで実現できる

Chapter5 データベースのチューニング

  • RDBMSは一貫性があることが強み。データのロックがあるので。
  • 性能重視ならNoSQLもあり。memcachedやRedisなど。
  • インデックスは便利だけど多すぎるとデータの追加・更新の負荷が増えるので注意
  • SQLにEXPLAINをつけて実行計画を確認しよう
  • SQL回数が多すぎるものはN+1問題を疑おう
  • N+1問題の解決策として、キャッシュ、別SQL、JOINなどが有効

Chapter6 リバースプロキシの利用

  • リバースプロキシはクライアントとアプリケーションサーバーの中継役
  • リバースプロキシを置かない場合、大量リクエストが来た場合、通信が遅いクライアントに専有されたりしてしまう
  • せいぜい1万リクエストが上限なのでC10K問題と呼ばれる
  • 代表的なリバースプロキシがnginx
  • gzipの設定などを行うことで高速化できる

Chapter7 キャッシュの活用

  • キャッシュにはアプリケーションで作成するものと、クライアントによるHTTPレスポンス自体のキャッシュがある
  • 本章ではアプリケーションによるキャッシュが対象
  • 代表的なキャッシュアプリはmemcachedとRedis
  • キャッシュをアプリケーションのメモリやファイルに保存する方法もあるが、並列の処理・有効期限・削除などに注意が必要
  • ただ、キャッシュはデータの不整合(古いデータを表示してしまう)など処理が複雑になるデメリットがある
  • キャッシュヒット、キャッシュミスの割合を監視して適切にキャッシュが使えているか監視しよう

Chapter8 押さえておきたい高速化手法

  • 開発用の設定で冗長なログを出力しない
  • 画像データをMySQLではなく静的ファイル配信しよう
  • urlにランダム文字列を付与(app.js?v=ramdom_stringなど)することでキャッシュを利用されないようにできる(キャッシュバスター)
  • HTTPヘッダーを活用してクライアント側にキャッシュさせる
    • cssはjsファイルなどはあまり変更ないのでキャッシュさせたい
    • If-Modified-Sinceヘッダーなどを利用しよう

Chapter9

Android開発でJavaバージョンに関するビルドエラー

ビルドエラーの内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
> Could not resolve all files for configuration ':classpath'.
> Could not resolve com.android.tools.build:gradle:7.4.2.
Required by:
project :
> No matching variant of com.android.tools.build:gradle:7.4.2 was found. The consumer was configured to find a runtime of a library compatible with Java 8, packaged as a jar, and its dependencies declared externally, as well as attribute 'org.gradle.plugin.api-version' with value '7.5' but:
- Variant 'apiElements' capability com.android.tools.build:gradle:7.4.2 declares a library, packaged as a jar, and its dependencies declared externally:
- Incompatible because this component declares an API of a component compatible with Java 11 and the consumer needed a runtime of a component compatible with Java 8
- Other compatible attribute:
- Doesn't say anything about org.gradle.plugin.api-version (required '7.5')
- Variant 'javadocElements' capability com.android.tools.build:gradle:7.4.2 declares a runtime of a component, and its dependencies declared externally:
- Incompatible because this component declares documentation and the consumer needed a library
- Other compatible attributes:
- Doesn't say anything about its target Java version (required compatibility with Java 8)
- Doesn't say anything about its elements (required them packaged as a jar)
- Doesn't say anything about org.gradle.plugin.api-version (required '7.5')
- Variant 'runtimeElements' capability com.android.tools.build:gradle:7.4.2 declares a runtime of a library, packaged as a jar, and its dependencies declared externally:
- Incompatible because this component declares a component compatible with Java 11 and the consumer needed a component compatible with Java 8
- Other compatible attribute:
- Doesn't say anything about org.gradle.plugin.api-version (required '7.5')
- Variant 'sourcesElements' capability com.android.tools.build:gradle:7.4.2 declares a runtime of a component, and its dependencies declared externally:
- Incompatible because this component declares documentation and the consumer needed a library
- Other compatible attributes:
- Doesn't say anything about its target Java version (required compatibility with Java 8)
- Doesn't say anything about its elements (required them packaged as a jar)
- Doesn't say anything about org.gradle.plugin.api-version (required '7.5')

原因

Java のバージョンが合っていない

IDEの設定

Android Studio>Settings>Build,Execution,Deployment>Build Tools>Gradle>Gradle JKDでjava11を設定する
パスをコピーしておく

プロジェクト本体の設定

gradle.propertiesファイルにコピーしたパスを追記する。
例えば以下など

1
/Library/Java/JavaVirtualMachines/openjdk-11.0.1.jdk/Contents/Home

RailsにRedisを導入した際のメモ

redisライブラリの導入

Gemfileに以下を追加する。

1
2
gem 'redis'
gem 'hiredis-client'

Railsのキャッシュの設定

  • Railsのキャッシュのデフォルトの保存先はtmpファイルになっている(キャッシュストアの設定
  • Redisを使うように変更するには以下のようにする

対象ファイル:config.enveronments/production.rb

変更内容:

1
2
3
4
config.cache_store = :redis_cache_store, {
url: ENV['REDIS_URL'],
ssl: true, # この行はAmazon ElastiCache For Redisの場合のみ。ローカルのDocker開発環境の場合は不要
}

ENV['REDIS_URL']について

ENV['REDIS_URL']については、それぞれ以下のように設定する。

Amazon ElastiCache For Redis の場合

redis://hoge.serverless.apne1.cache.amazonaws.com:6379/0

ローカルのDocker開発環境の場合

redis://redis:6379/0

docker-compose.ymlに以下のように追記する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
web:
environment:
REDIS_URL: 'redis://redis:6379/0'
depends_on:
- redis

redis:
image: redis:latest
ports:
- "6379:6379"
volumes:
- redis-data:/data

volumes:
redis-data:
driver: local

キャッシュを利用するコード

上記の設定完了後、以下のコードでキャッシュが効くようになる。

1
2
3
4
5
6
key = "cache_count_" + params[:id]
result = Rails.cache.fetch(key, expires_in: 5.minutes) do
# キャッシュがなければここが実行される
count = get_count(params[:id])
count
end

と、基本的にはこれだけでOK。

注意点

  • Redisでは正規表現を使ってキャッシュを削除することができる
  • 例えばRails.cache.delete_matched('cache_count_*')
  • ただし、Amazon ElastiCache For Redis は正規表現を使ってキャッシュを削除することができない
  • Amazon ElastiCache For Redis はクラスター構成となっていて、キャッシュの保存先が分散されているため
  • 以下のエラーが発生する
  • CROSSSLOT Keys in request don't hash to the same slot

リストやハッシュをキャッシュする場合

  • 上記の設定では、キャッシュの対象は文字列のみ
  • リストやハッシュをキャッシュする場合は、$redisインスタンスを直接利用する必要がある

$redisの設定

config/initializers/redis.rbを作成し、以下のように記述する。

1
$redis = Redis.new(url: ENV['REDIS_URL'])

リストをキャッシュする場合

1
2
$redis.lpush('list_key', 'value')
$redis.lrange('list_key', 0, -1)

注意点

sslを有効にする場合、Rails.cacheではssl: trueのオプションがあったが、$redisではurlrediss://を指定する必要がある。
例えば、rediss://hoge.serverless.apne1.cache.amazonaws.com:6379/0とすると、sslが有効になる。

VuetifyのバリデーションをJavaScriptで実行する

html

1
2
3
4
5
<v-form ref="form">
<v-text-field
:rules="[rules.required]"
></v-text-field>
</v-form>

JavaScript

1
2
3
4
5
6
7
8
9
10
11
12
13
// バリデーション
let form = ref(null)
const rules = ref({
required: value => !!value || '',
})

const submit = async () => {
const validResult = await form.value.validate()
if (!validResult.valid) {
return
}
//以下、submitの処理
}

【kintone】プラグインアップロード時のタイムアウトエラー

kintone-plugin-uploaderコマンド実行時に以下のエラーが出ました。

エラーが発生しました TimeoutError: Waiting for selector .ocean-ui-dialog failed: Waiting failed: 60000ms exceeded

原因はplugin.zipの指定方法が間違ってました。

以下の通りに修正したらうまくいきました。

1
2
3
4
# 修正前
kintone-plugin-uploader plugin.zip
# 修正後
kintone-plugin-uploader dist/plugin.zip

言われてみればそうなのですが、エラーからは読み取りにくくて苦戦しました。。

Railsでアプリログのフォーマットを変更する方法

Railsでアプリログのフォーマットを変更する方法です。

以下の2ファイルを追加・修正します。

(追加)app/lib/local_dev_log_formatter.rb

1
2
3
4
5
6
7
class LocalDevLogFormatter < ActiveSupport::Logger::SimpleFormatter
def call(severity, timestamp, progname, msg)
formatted_severity = sprintf("%-5s", "#{severity}")
formatted_time = timestamp.strftime("%Y-%m-%d %H:%M:%S.") << timestamp.usec.to_s[0..2].rjust(3)
"[#{formatted_severity} #{formatted_time} ##{Process.pid}] #{msg}\n"
end
end

(修正)config/environments/development.rb

1
2
3
4
5
6
7
8
9
Hoge::Application.configure do

# 中略

config.log_formatter = LocalDevLogFormatter.new # 日時を出力するためにカスタムフォーマッターを指定
logger = ActiveSupport::Logger.new("log/#{Rails.env}.log", 5, 100 * 1024 * 1024)
logger.formatter = config.log_formatter
config.logger = ActiveSupport::TaggedLogging.new(logger)
end

app/lib配下に配置することとconfig.loggerの方にも設定が必要なところが分からなくてハマりました。。

Faraday::Error::ConnectionFailedエラーの解消

エラー

1
2
3
rake aborted!  
NameError: uninitialized constant Faraday::Error::ConnectionFailed
Did you mean? Faraday::ConnectionFailed

解決策

1
2
3
4
5
6
7
# cdする先のディレクトリは人によって違う。下のコマンドで確認できる。
# bundle show rails
cd /Users/hoge/.rbenv/versions/3.0.5/lib/ruby/gems/3.0.0/gems
grep -r "Faraday::Error::" .
sed -i '' 's/Faraday::Error::/Faraday::/g' ./elasticsearch-api-7.4.0/Rakefile
sed -i '' 's/Faraday::Error::/Faraday::/g' ./elasticsearch-transport-7.4.0/spec/elasticsearch/transport/client_spec.rb
sed -i '' 's/Faraday::Error::/Faraday::/g' ./elasticsearch-transport-7.4.0/lib/elasticsearch/transport/transport/http/faraday.rb

googleの翻訳APIをrailsで実行する方法

はじめに

googleの翻訳APIをrailsで実行する方法です。
APIキーを使えばjs側でも実行できますが、そうするとAPIキーがユーザーに漏れてしまうので、Railsなどのサーバーを経由するようにしましょう。

jsのコード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
async translate(text) {
const url = '/translate';
try {
const response = await axios.post(url, {
text: text,
});

const data = response.data;
if (data.error) {
console.error('Error:', data.error);
return '';
} else {
return data.translation_text;
}
} catch (error) {
console.error('Error:', error.message);
return '';
}
},

Rails

gemインストール

Gemfileに以下の行を追加する

1
gem 'google-cloud-translate-v2'

ターミナルで以下のコマンドを実行する

1
bundle install

認証の設定

APIキーを利用することもできるのですが、gemライブラリを利用する場合はAPIキーではなく認証情報を利用する方法が便利です。

  1. GCPを開く
  2. API Managerを開く
  3. 認証情報を開く
  4. 対象のサービスアカウントをクリックもしくは新しく追加
  5. 「キー」タブからjsonをダウンロード
  6. プロジェクト直下にsecret_keyフォルダを生成
  7. secret_keyフォルダにjsonを配置する

参考
https://github.com/googleapis/google-cloud-ruby/blob/main/google-cloud-translate-v2/AUTHENTICATION.md

ルーティング

routes.rb

1
2
3
Rails.application.routes.draw do
post 'translate', to: 'google_api/translation#translate'
end

controllerのコード

  1. controllersフォルダの直下にgoogle_apiフォルダを作成する
  2. google_apiフォルダにtranslation_controller.rbファイルを作成する
1
2
3
4
5
6
7
8
9
10
11
12
13
require 'google/cloud/translate/v2'

class GoogleApi::TranslationController < ApplicationController
def translate
ENV["TRANSLATE_PROJECT"] = "hoge-project"
ENV["TRANSLATE_CREDENTIALS"] = "secret_key/hoge.json"
client = Google::Cloud::Translate::V2.new
translation = client.translate text, to: 'en'
render json: { translation: translation.text }
rescue Google::Cloud::Error => e
render json: { error: e.message }, status: :internal_server_error
end
end
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×