python 3.6 で secure に random string を生成する方法

python 3.6 には secrets というのが追加されていてこれを使うのが一番簡単です。
15.3. secrets — Generate secure random numbers for managing secrets — Python 3.6.4 documentation

もしもurl-safeな文字列を所望するのであれば、こんな感じ。

token = secrets.token_urlsafe(32)

tokenの生成などに気軽に使えるので便利です。

3.5以前だと、os.urandom もしくは random.SystemRandom などを使って生成してください。

Redis Cluster(3.x ~ 4.x) での Pub/Sub

揮発性のあるtopic(channel)に対してのPub/Subが欲しい案件が出てきたので、Redis ClusterでPub/Subでもしようかと思っていたのでちょっと調べてみた。そのメモ書き。

Redis pub/subの内部的な話し

このブログが参考になる。
https://making.pusher.com/redis-pubsub-under-the-hood/

Redis Cluster での Pub/Sub

現状のRedis clusterでのpub/subの仕様。

https://redis.io/topics/cluster-spec#publishsubscribe

In a Redis Cluster clients can subscribe to every node, and can also publish to every other node. The cluster will make sure that published messages are forwarded as needed. The current implementation will simply broadcast each published message to all other nodes, but at some point this will be optimized either using Bloom filters or other algorithms.

あかん。
これだと、Redis Clusterにnode足せば出すほど、networkがボトルネックになっていく。

issueにもあった。
https://github.com/antirez/redis/issues/2672

Bloom filterを使った最適化の予定があるみたいなので、それを待つことになりそう。

Redis 4.2のroadmapに、DisqueをRedis moduleとして作り直すとあるので、もしかしたらそれと関連して4.2でなんかしらの修正が入りそう。
https://gist.github.com/antirez/a3787d538eec3db381a41654e214b31d

Redis Cluster での Lua script に関して

Redis Cluster の Slot に関して

Redis Cluster は slot(shard) が 16384個あり、それぞれの node がその slot を複数保持している。 ある key がどの slot に割当られるのかは、以下の計算で行われる。

slot_num = CRC16(key) mod 16384

また、key に {...} を使用することで slot の計算に使われる部分を指定することができる。

  • {userId10000}:score
  • {userId10000}:friends

は、同じ slot に含まれる。

Lua script with Redis Cluster

Redis Clusterでも、 KEYS が全て同じ slot なら Lua script が使える 。

例えば、こういうのは大丈夫。

> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 {key1}hoge {key1}bar first second
1) "{key1}hoge"
2) "{key1}bar"
3) "first"
4) "second"

が、 keys に異なるslotが含まれると redis cluster は処理することができない。

> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 {key1}hoge {key2}bar first second
(error) CROSSSLOT Keys in request don't hash to the same slot

Spring boot の configuration 周りで意識したいクラス命名規則

xxxConfigだったりxxxSettingだったりxxxConfigurationだったりxxxPropertiesだったり、人によってバラバラなのでなかなか困ります。

本家でどうしているのかを見てみると下のような感じです。このようにしたほうがわかりやすいですぞ〜。

@Configurationをつけるクラス

xxxConfiguration。 annotationの名前と対応しているのでわかりやすい!

@ConfigurationPropertiesをつけるクラス

xxxProperties。 同じくannotationの名前と対応しているのでわかりやすい!

Spring の UriComponentsBuilder のちょっと罠っぽいところ

UriComponentsBuilder は以下のように、URIの構築を簡単にできるやつです。 Spring とか使っている人だと使ったことあると思います。

String result = UriComponentsBuilder
        .fromUriString("https://example.com/")
        .queryParam("hoge", "あ")
        .toUriString();

assertThat(result).isEqualTo("https://example.com/?hoge=%E3%81%82");

最近使っていて気づいたのですが、UriComponentsBuilder.fromUriString(str) or UriComponentsBuilder.fromHttpUrl(str) に渡す URI string に query がついている場合、その部分も encode されるという挙動をします。 つまり、予め encodeしていると2重に encode されてしまうわけです。

こんな感じです。

String result = UriComponentsBuilder
        .fromHttpUrl("https://example.com/?nextPath=%2Fpath%2Fto%2Fsome%2Fpage") // "/path/to/some/page"
        .queryParam("hoge", "あ")
        .toUriString();

assertThat(result).isNotEqualTo(
        "https://example.com/?nextPath=%2Fpath%2Fto%2Fsome%2Fpage&hoge=%E3%81%82");
assertThat(result).isEqualTo(
        "https://example.com/?nextPath=%252Fpath%252Fto%252Fsome%252Fpage&hoge=%E3%81%82");

んー。個人的には、queryParam で指定した部分だけのほうが直感的だと思いますが...。

参考までに、Apache HttpClient にある URIBuilder だと query 部分が2重にencodeされるということはありません。

String result = new URIBuilder("https://example.com/?nextPath=%2Fpath%2Fto%2Fsome%2Fpage")
        .addParameter("hoge", "あ")
        .build().toString();
assertThat(result).isEqualTo(
        "https://example.com/?nextPath=%2Fpath%2Fto%2Fsome%2Fpage&hoge=%E3%81%82");