gensimの読み込みで実行時エラーが発生する

意外と日本語で検索にヒットしないのでメモ。

発生したエラーはこんな感じ

>>> from gensim.models import word2vec
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.7/site-packages/gensim/__init__.py", line 5, in <module>
    from gensim import parsing, corpora, matutils, interfaces, models, similarities, summarization, utils  # noqa:F401
  File "/usr/local/lib/python3.7/site-packages/gensim/corpora/__init__.py", line 6, in <module>
    from .indexedcorpus import IndexedCorpus  # noqa:F401 must appear before the other classes
  File "/usr/local/lib/python3.7/site-packages/gensim/corpora/indexedcorpus.py", line 15, in <module>
    from gensim import interfaces, utils
  File "/usr/local/lib/python3.7/site-packages/gensim/interfaces.py", line 21, in <module>
    from gensim import utils, matutils
  File "/usr/local/lib/python3.7/site-packages/gensim/matutils.py", line 1076, in <module>
    from gensim._matutils import logsumexp, mean_absolute_difference, dirichlet_expectation
  File "__init__.pxd", line 872, in init gensim._matutils
ValueError: numpy.ufunc has the wrong size, try recompiling. Expected 192, got 216
>>> 

インストールされているバージョンは 3.6.0

$ pip show gensim
Name: gensim
Version: 3.6.0
Summary: Python framework for fast Vector Space Modelling
Home-page: http://radimrehurek.com/gensim
Author: Radim Rehurek
Author-email: me@radimrehurek.com
License: LGPLv2.1
Location: /usr/local/lib/python3.7/site-packages
Requires: scipy, numpy, six, smart-open
Required-by: 

バージョンを一個前(3.5.0)に戻したらエラーが消えた

$ pip install gensim==3.5.0

そもそも他の人でも起きているのか謎い。。 ただ、Docker環境を作って検証したけど発生したのできっと他にも困っている人がいるはず!

MySQLのインデックスは1つのテーブルにつき1つしか使われないとは限らない

タイトルが全てですが、その通り。 MySQLにはインデックスマージというものが存在するということを最近知った。

詳細は以下のドキュメントを参照してほしい。

MySQL :: MySQL 5.6 リファレンスマニュアル :: 8.2.1.4 インデックスマージの最適化

必ずしも使われるわけではないけど、1つのテーブルにつきインデックスは1つしか使われないものと思っていた認識が誤りだった。

create table user(
    id int,
    last_name varchar(20),
    first_name varchar(20),
    nickname varchar(20)
) ENGINE = InnoDB DEFAULT CHARSET = utf8;

こんな感じのテーブルがあるとしてインデックスを貼ります。

CREATE INDEX idx_first_name ON user(first_name); 
CREATE INDEX idx_last_name ON user(last_name); 
CREATE INDEX idx_nickname ON user(nickname); 

データを何件か入れて以下のSQLを実行して実行計画を取得する。

explain
select *
from user
where first_name like 'テスト%' or last_name like 'テスト%' or nickname like 'テスト%';

インデックスを貼っていない場合

+------+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+------+-------------+-------+------+---------------+------+---------+------+------+-------------+
|    1 | SIMPLE      | user  | ALL  | NULL          | NULL | NULL    | NULL |  200 | Using where |
+------+-------------+-------+------+---------------+------+---------+------+------+-------------+

インデックスを貼っている場合

+------+-------------+-------+-------------+-------------------------------------------+-------------------------------------------+----------+------+------+--------------------------------------------------------------------------+
| id   | select_type | table | type        | possible_keys                             | key                                       | key_len  | ref  | rows | Extra                                                                    |
+------+-------------+-------+-------------+-------------------------------------------+-------------------------------------------+----------+------+------+--------------------------------------------------------------------------+
|    1 | SIMPLE      | user  | index_merge | idx_first_name,idx_last_name,idx_nickname | idx_first_name,idx_last_name,idx_nickname | 63,63,63 | NULL |    3 | Using sort_union(idx_first_name,idx_last_name,idx_nickname); Using where |
+------+-------------+-------+-------------+-------------------------------------------+-------------------------------------------+----------+------+------+--------------------------------------------------------------------------+

では、複合インデックスだとどうなるか?既存のインデックスを消して以下のインデックスを追加し、確認する

CREATE INDEX idx_multi ON user(first_name, last_name, nickname); 
+------+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id   | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+------+-------------+-------+------+---------------+------+---------+------+------+-------------+
|    1 | SIMPLE      | user  | ALL  | idx_multi     | NULL | NULL    | NULL |  200 | Using where |
+------+-------------+-------+------+---------------+------+---------+------+------+-------------+

なるほど。orで検索する場合は、複合インデックスを使っても使われることはなく1つ1つのカラムに対してインデックスを貼ることでインデックスマージが効いてパフォーマンスが上がる可能性が高いということらしい。 今回のケースでは Using sort_union が使われているが公式ページを見ると他にも種類があるようです。

今回の機能はMySQL5以降は有効とのことでMariaDBMySQL両方で試してみました。こうゆうとき、dockerとか便利ですよね〜と言いたいとこだったけど最新版のMariaDBでエラーが発生して時間かかった。結局バージョンを10.0まで落とすことで解決したけど。

docker-compose.yml

version: '2'

services:
  maria:
    image: mariadb:10.0
    restart: always
    volumes:
      - ./data/mariadb/:/var/lib/mysql
    ports:
      - "3307:3306"
    expose:
      - "3307"
    environment:
      MYSQL_ROOT_PASSWORD: root
    user: "1000:50"
  mysql:
    image: mysql
    restart: always
    volumes:
      - ./data/mysql:/var/lib/mysql
    ports:
      - "3308:3306"
    expose:
      - "3308"
    environment:
      MYSQL_ROOT_PASSWORD: root

mariadb:10.2 で出ていたエラーは以下の通り。時間があったら調べよう

[Warning] InnoDB: Failed to set O_DIRECT on file./ibdata1;OPEN: Invalid argument, ccontinuing anyway. O_DIRECT is known to result in 'Invalid argument' on Linux on tmpfs, see MySQL Bug#26662.
[Note] InnoDB: Highest supported file format is Barracuda.
[Note] InnoDB: 128 out of 128 rollback segments are active.
[Note] InnoDB: Creating shared tablespace for temporary tables
[Note] InnoDB: Setting file './ibtmp1' size to 12 MB. Physically writing the file full; Please wait ...
[Note] InnoDB: File './ibtmp1' size is now 12 MB.
[Note] InnoDB: Waiting for purge to start
[Note] InnoDB: 5.7.21 started; log sequence number 1603633
[Note] InnoDB: Loading buffer pool(s) from /var/lib/mysql/ib_buffer_pool
[Note] InnoDB: Buffer pool(s) load completed at 180304  4:52:55
[Note] Plugin 'FEEDBACK' is disabled.
[ERROR] Could not open mysql.plugin table. Some plugins may be not loaded
[Note] Recovering after a crash using tc.log
[ERROR] Can't init tc log
[ERROR] Aborting

atomで設定を分けて起動する方法

Atom をメインで使っているとソースを書くために使ったりマークダウンエディタとして使ったりします。そのとき、パッケージをまとめて1つの設定に入れても良いんですが、色々な言語のautocomplate系を入れるのが嫌なときやマークダウンエディタとして使いたいときに設定を分けて起動することができます。

ここからはmacをベースにします。

atomの設定は~/.atomにあります。これを環境変数ATOM_HOMEで指定することで設定を分けることができます。環境変数を指定してatomコマンドをこんな感じで呼んでやればATOM_HOMEで指定した設定で起動します。

ATOM_HOME=~/.atom_markdown atom

このコマンドを.bash_profileエイリアスで登録しておくと捗ります。

alias atommd='ATOM_HOME=~/.atom_markdown atom'

.bash_profileを読み込んだら以下のコマンドを実行すると~/.atom_markdownの設定でatomが起動します。

$ atommd .

Eclipse環境での開発サポート

一人で開発している分にはあまり気になりませんが複数人で開発するときに書き方がバラバラだったり、レビューが大変だったりします。コード規約を作っても読まなければならず徹底するのは大変です。 そこで、Eclipseで利用できる開発サポートツールをいくつか紹介します。

Checkstyle

まずは、Checkstyleです。これはPleiadesを使っていればバンドルされています。Checkstyleは静的コード解析ツールでルール通りに記載されているかを自動でチェックしてくれるツールです。デフォルトでは、google_checks.xmlが有効になっています。自分好みの設定をすることも可能です。ちなみに僕はタブ区切りでも良いようにルールを変更しています。

Findbugs

FindbugsPleiadesにバンドルされています。これもCheckstyleと同様、静的コード解析ツールですがこちらはバグになりそうな可能性のあるコードを見つけてくれます。

Eclipse Metrics

あと個人的には、Eclipse Metricsというものを入れています。これは、ソースの複雑度(クラスやメソッドの行数、ネストの深さなど)を計測できるツールです。警告されれば、クラスの責務やメソッドの凝集度などを考える機会になります。 Pleiadesにはバンドルされていなかったので以下のページを参考にインストールしました。

www.ibm.com

これらを使わなくても開発は可能です。ですが、あった方が保守しやすいコードが書けるかと思います(何ヶ月か前に書いた自分のコードはもはや他人のコードです)。課題としては、設定内容をファイル化してバージョン管理できるようにすれば変更を追跡できて、他の人の環境にも容易に反映することができます。 これらをほぼ網羅でき設定ファイルの共有も楽なRubyGemsrubocopは素晴らしいですね。

Spring Bootでoauthのログインセッションをredisに突っ込む

タイトル通りです。 前にoauth認証でのログインができるようにしましたが、これだとログインセッションのあるサーバでしかログイン状態を維持できません。これだとスケールしないので外部に保存します。そこでredisを使ってスケールする仕組みを作りたいと思います。

主に参考にさせてもらったのはこちら。

qiita.com

ただ、oauth認証のコードをベースにしていると以下のようなエラーが出ました。

2017-10-18 21:32:50.838 ERROR 52558 --- [io-8080-exec-10] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception

org.springframework.data.redis.serializer.SerializationException: Could not write JSON: Error creating bean with name 'scopedTarget.accessTokenRequest': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request. (through reference chain: org.springframework.security.oauth2.client.DefaultOAuth2ClientContext["accessTokenRequest"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Error creating bean with name 'scopedTarget.accessTokenRequest': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request. (through reference chain: org.springframework.security.oauth2.client.DefaultOAuth2ClientContext["accessTokenRequest"])
    at org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.serialize(GenericJackson2JsonRedisSerializer.java:99) ~[spring-data-redis-1.8.7.RELEASE.jar:na]
    at org.springframework.data.redis.core.AbstractOperations.rawHashValue(AbstractOperations.java:171) ~[spring-data-redis-1.8.7.RELEASE.jar:na]
    at org.springframework.data.redis.core.DefaultHashOperations.putAll(DefaultHashOperations.java:129) ~[spring-data-redis-1.8.7.RELEASE.jar:na]
    at org.springframework.data.redis.core.DefaultBoundHashOperations.putAll(DefaultBoundHashOperations.java:86) ~[spring-data-redis-1.8.7.RELEASE.jar:na]
    at org.springframework.session.data.redis.RedisOperationsSessionRepository$RedisSession.saveDelta(RedisOperationsSessionRepository.java:778) ~[spring-session-1.3.1.RELEASE.jar:na]
    at org.springframework.session.data.redis.RedisOperationsSessionRepository$RedisSession.access$000(RedisOperationsSessionRepository.java:670) ~[spring-session-1.3.1.RELEASE.jar:na]
    at org.springframework.session.data.redis.RedisOperationsSessionRepository.save(RedisOperationsSessionRepository.java:388) ~[spring-session-1.3.1.RELEASE.jar:na]
    at org.springframework.session.data.redis.RedisOperationsSessionRepository.save(RedisOperationsSessionRepository.java:245) ~[spring-session-1.3.1.RELEASE.jar:na]
    at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.commitSession(SessionRepositoryFilter.java:245) ~[spring-session-1.3.1.RELEASE.jar:na]
    at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.access$100(SessionRepositoryFilter.java:217) ~[spring-session-1.3.1.RELEASE.jar:na]
    at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:170) ~[spring-session-1.3.1.RELEASE.jar:na]
    at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:80) ~[spring-session-1.3.1.RELEASE.jar:na]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106) ~[spring-boot-actuator-1.5.7.RELEASE.jar:1.5.7.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1457) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_121]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_121]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.20.jar:8.5.20]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_121]
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Error creating bean with name 'scopedTarget.accessTokenRequest': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request. (through reference chain: org.springframework.security.oauth2.client.DefaultOAuth2ClientContext["accessTokenRequest"])
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:388) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:348) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:343) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:697) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeWithType(BeanSerializerBase.java:580) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer.serialize(TypeWrappedSerializer.java:32) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:292) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:3697) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsBytes(ObjectMapper.java:3097) ~[jackson-databind-2.8.10.jar:2.8.10]
    at org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.serialize(GenericJackson2JsonRedisSerializer.java:97) ~[spring-data-redis-1.8.7.RELEASE.jar:na]
    ... 37 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.accessTokenRequest': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:355) ~[spring-beans-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:192) ~[spring-aop-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at com.sun.proxy.$Proxy94.isEmpty(Unknown Source) ~[na:na]
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeWithType(MapSerializer.java:550) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeWithType(MapSerializer.java:30) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:706) ~[jackson-databind-2.8.10.jar:2.8.10]
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:689) ~[jackson-databind-2.8.10.jar:2.8.10]
    ... 43 common frames omitted
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
    at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.springframework.web.context.request.AbstractRequestAttributesScope.get(AbstractRequestAttributesScope.java:41) ~[spring-web-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:340) ~[spring-beans-4.3.11.RELEASE.jar:4.3.11.RELEASE]
    ... 51 common frames omitted

検索してもドンピシャな記事がなく色々と試行錯誤した結果以下のページに行き着きました。 以下のページを参考にRedisTemplate を作成しました。合わせてHttpSessionConfigクラスを削除すればredisに登録することができます。

Spring Boot Redis custom json serializer example. · GitHub

実装した結果はこちらです。

github.com