OmniAICreator/Qiita-1.07M huggingface dataset:
AKS 上にデプロイした httpd で Azure Files をドキュメントルートに指定すると異常なレスポンスを返却することがある

AKS (Azure Kubernetes Service) で PV を利用する場合、Azure ディスクか Azure Files が主な選択肢となります。Azure Files を PV として扱う際に利用する [Azure Files CSI Driver](https://github.com/kubernetes-sigs/azurefile-csi-driver/blob/master/docs/driver-parameters.md) は、内部的には SMB もしくは NFS でネットワークマウントを行っています。

一方、httpd にはネットワークマウント時に考慮しなければならない設定項目があります。その結果、ドキュメントルートとして Azure Files を指定すると、ネットワークマウントに起因する問題が発生し異常な（冒頭が欠落する等）レスポンスを返却することがあります。

## ネットワークマウントに起因する問題を修正する

ネットワークマウント時に考慮しなければならない設定項目は2点です。configMap 等を通じて以下の設定となるよう修正してください。

### EnableSendfile

この項目は httpd がクライアントにファイルの内容を返す際に `sendfile(2)` システムコールを使うか否かを制御します。

`sendfile(2)` システムコールは、あるファイル記述子から別のファイル記述子へデータをコピーします。同様の処理はアプリケーション側で `read(2)` システムコールと `write(2)` システムコールを組み合わせることでも実現できますが、カーネル空間とユーザ空間の間でデータ転送が発生します。一方、`sendfile(2)` では処理がカーネル内で完結するため、無駄な転送が発生せず効率的です。[^1]

[^1]: https://man7.org/linux/man-pages/man2/sendfile.2.html

そのため、デフォルト設定では `sendfile(2)` を使用する設定が有効化されています。しかしマニュアルでは、DocumentRoot としてネットワークマウントされたディレクトリが指定されている場合、カーネルがキャッシュを通してファイルを提供できない場合があると記載されています。[^2] マニュアルに従い、以下のように設定しましょう。

[^2]: https://httpd.apache.org/docs/2.4/en/mod/core.html#enablesendfile

```conf
EnableSendfile Off
```

なお、[stackoverflow のこの質問](https://stackoverflow.com/questions/46367130/why-does-apache-recommend-against-using-sendfile-with-nfs-on-linux)ではネットワークマウントで EnableSendfile をオフにすることが推奨とされた経緯について推測を交えながら説明がなされており、興味深いです。

### EnableMMAP

この項目は httpd がファイルを読み込む際、メモリマッピングを使うかどうかを制御します。

DocumentRoot として NFS でマウントされたディレクトリが指定されている場合、クラッシュする可能性があるとされています。メモリマップされたファイルが削除・トランケートされたとき、セグメンテーションフォールトが発生するためです。[^3]

[^3]: https://httpd.apache.org/docs/2.4/en/mod/core.html#enablemmap

マニュアルでは明記されていませんが、SMB の場合も EnableMMAP を Off にしなければならない症例が報告されていますので、どちらにせよ以下のように設定しましょう。[^4] [^5] [^6]

[^4]: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=900821

[^5]: https://q.hatena.ne.jp/1263439075#c231883

[^6]: https://bz.apache.org/bugzilla/show_bug.cgi?id=46688

```conf
EnableMMAP Off
```

## 参考リンク

* [core - Apache HTTP サーバ バージョン 2.4](https://httpd.apache.org/docs/2.4/mod/core.html)
* [Apache HTTP Server en AKS con Azure Files](https://www.returngis.net/2021/06/apache-http-server-en-aks-con-azure-files/)
* [Why does Apache recommend against using sendfile() with NFS on Linux](https://stackoverflow.com/questions/46367130/why-does-apache-recommend-against-using-sendfile-with-nfs-on-linux)
* [apache reads wrong data over cifs filesystems served by samba](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=900821)
Error: Member not found: ’packageRoot’ in Flutterの対処方法

## Flutterのバージョンあげたらエラーが出た
半年ぶりくらいにFlutterのバージョンを上げたら、以下のエラーが出ました。
・Flutterのバージョンを2.10.3に更新
・Visual Studio Codeにてビルドを実施

>: Error: Member not found: 'packageRoot'.
../…/interface/local_platform.dart:46
          io.Platform.packageRoot; // ignore: deprecated_member_use


## flutter pub upgradeすれば直るけど・・・
ググると「flutter pub upgradeすると直る」という情報がたくさん出てくるんです。
そして、記事通りに「flutter pub upgrade」をすると直るんです。
直るんですが、pubspec.lockファイルが更新されてしまうんです。。。

自分は、組み込んでいるpackageのバージョンによって挙動が変わってしまうのは嫌なので、
pub.devからpackageを入れる際は、常にバージョンを指定していれるようにしています。

そして、「flutter pub upgrade」してしまうと、指定したバージョンを無視して最新のものを入れられてしまうのです。
せっかく、ソースコードでバージョン指定しているのに、そのバージョンと異なったバージョンのものが
アプリに組み込まれてしまうのは、正直いただけないのでこのやり方は却下でした。

## 辿り着いたこのサイト
本件を解決するために、何箇所のサイトを開いたか忘れました。
(そして、何度「flutter pub upgareすればヨシ！」というサイトをみたことか・・・)

そして本日、以下のサイトに出会えました。
https://www.fluttercampus.com/guide/270/error-member-not-found-packageroot-flutter/

ここに記載されていた **Solution 3: Overrides Dependency** が、自分の中ではベストアンサーでした。

## 修正方法
pubspec.yamlファイルに以下の行を追加します。

```yaml
dependencies:
  flutter:
    sdk: flutter
  (中略)

// 以下を追加
dependency_overrides:
  platform: ^3.1.0
```

## dependency_overridesとは
いったん、自分がしたかったpubspec.lockに影響を及ぼさない修正はこれで達成できました。
しかし、この修正は安全なのか。dependency_overridesとはなんなのか。疑問が残りました。

[Flutter 公式サイト](https://dart.dev/tools/pub/dependencies)に詳しく説明がありますが、自分が利用しているパッケージの中で参照しているパッケージのバージョンを指定できる機能のようです。(間違ってたらご指摘ください)
そして、そのパッケージが保証しているバージョンの範囲を超えたものを指定してしまうと、アプリが壊れる可能性もあるようです。
いったん自分の場合は大丈夫そうでしたが、利用にはリスクがありそうだということを念頭に入れておいたほうが良さそうです。

理解が乏しいので、詳しい方がいらっしゃいましたらコメントお願いします。
【LinuC1合格体験記】完全未経験からLinuCレベル1を取得した話

はじめまして、未経験でインフラエンジニアに飛び込み、研修の一環として資格取得に励む者です。

先日LinuC101,102(別日)を受け、無事合格。(800点満点中、101は720点、102は680点)
勉強期間は合計で3週間、時間にして100時間弱ほど。

Linuxに触ることなく、LinuCを取得してしまいました。

勉強方法は概ね、Ping-tを繰り返して知識を詰め込むだけでした。(+あずき本などの模試)
ただ、同じような境遇で取得を目指す方もいると思うので、自分なりに躓いたところなど、記事にしてみたいと思います。

参考になれば幸いです。

# 勉強法

まず最初に、「1週間でLPICの基礎が学べる本」を一通り読みました(1日目)。

https://www.amazon.co.jp/dp/429500636X

Linux何もわからなかったので、読んでおいてよかったなーと思います。
基本的な語句の意味がわかるようになります。

(例えば、カーネルとか、マウントとか、最初は意味がわかりませんでした。このあたりのまとまった知識が得られるので、未経験者はぜひ。ブックオフにもありましたよ)

ちなみに、タイトルのLPICはLinuCとほぼ同じ資格です。
LPICが大元で、日本でLinuCに分裂した、という感じ。

その後、あずき本を読もうとするも、意味がわからなすぎて挫折。

https://www.amazon.co.jp/dp/4798166162

この本、他の方の体験記見るとわかりやすいという意見が多いのですが、初学者としては、辞書みたいに淡々と覚えることが書かれた「The教科書」という印象を受けました。

なんだかんだ最後までお世話になったのは事実。買うとお高いので、常備している会社に感謝。

個人的にはスピードマスターの方がおすすめ。演習問題が解説付きで大量にあります。
問題演習はPing-tでも事足りるので、教科書持っておきたい方はあずき本で。

https://www.amazon.co.jp/dp/4798166154

そして、前述の通り、ひたすらPing-tを解くように。
具体的な勉強方法は以下の通り。

1. Ping-tで「解説」や「参考」確認しながら解く(一周するまでは正答率0%~30%くらいです)
1. 全部ヒット・コンボになるまで一、二周
1. コマ問一周(章を小分けにするとやる気がマシになります。正答率30%など普通に出ますが、折れないで頑張りましょう)

私の場合、普通の問題をやるより、コマ問でカタカタやったほうが定着しやすかったです。
勉強し始めは解説と無限にらめっこできついですが、なんとか耐えしのぎましょう。

可能なら、ひとまとまり(単元、章など)の勉強は、まとまった時間でやったほうがいいです。途中で日を跨ぐと、人間の脳がアホすぎて忘れます。(※時間的にしょうがない日はありましたし、勉強が億劫になるよりは少しでもやるのが大事)

コマ問は、やっておくと点数安定すると思います。本試験で出る入力問題はPing-tほど難しくないので、だいたい取れます。

最後の章まで終わったら仕上げていく段階。好みや気分で以下のことやってました。
- あずき本、スピードマスターの演習
- Ping-t模試やって、間違えた問題をお気に入りにポイ
- コマ問も80%とれるくらいまで。もとい飽きるまで(個人的にコマ問がいちばん楽しかった)

あずき本やスピードマスターを持っている方は、仕上げが終わったら本の模試をやりましょう。
模試は各本1回分しかないので、最後まで温存がおすすめです。(私は2冊とも借りていたので、一通り勉強して片方、仕上げ終わってもう片方やりました)

ちなみに本の模試は結構難しめに作られてるので、思ったより点数低くても大丈夫だと思います。
私の場合、模試で80%、本試験で85~90%程度でした。

# 細かいところ

## 会場で写真撮ります
ベンダー試験初めてでびっくりしたのが、本人確認だけでなく写真撮影までしたこと。
試験後にもらえる白黒の試験レポートに写真載ってるせいで、安易に見せられないです。(ヤバめの顔が写ってます)

あと、本人確認書類は写真つきでないとダメで、マスク外して顔確認もします。
忘れると受けられない上返金もされないみたいなので、お気をつけて。

(当日、家に取りに行かれた方もいました。もし忘れても救いがあるかもしれないので、家の近くで受けておくと良いかも)

## オプションはコマ問かPing-tの表を保存して覚えた
-fみたいな素直なオプション大好き。だいたい、強制的にという意味でした。
findは-name(ハイフン一個)なのに、rpmは--install(ハイフン二個)なのややこしすぎる(?)。

このあたり、いくらやっても覚えようがなかったので、コマ問死ぬほどやってました。安定して80%~90%取れるくらい…

あとは、Ping-tの参考・解答の表を手で隠しながら覚えて(英単語覚えるときみたいな感じ)、なんとか乗り切りました。

## オープンソースの文化は3問のみだった
正答率は33%でした。

受ける人によって問題数変わると思うので、沼らない程度に勉強するのが良いです。
ping-tも教科書も必死に読んでこの点数なので、精神的ショックが大きかったです。
ブラウザのメンテURL

ブラウザのメンテ用URLが紛失するのでメモ

対象：Edge, Chorme

### Edge

・各種ＵＲＬリスト
[edge://edge-urls/](edge://edge-urls/)
・ポリシー
[edge://policy](edge://policy)
・フラグ
[edge://flags/](edge://flags/)
・セッティング
[edge://settings](edge://settings)
・バージョン確認
[edge://compat/enterprise](edge://compat/enterprise)
・互換性
[edge://version/](edge://version/)


### Chorme

・各種ＵＲＬリスト
[chrome://chrome-urls/](chrome://chrome-urls/)
・ポリシー
[chrome://policy/](chrome://policy/)
・フラグ
[chrome://flags/](chrome://flags/)
・セッティング
[chrome://settings/](chrome://settings/)
・バージョン
[chrome://version/](chrome://version/)

###履歴
2022/3/18 作成
2023/9/1 Edgeの互換性を追加
Blender作られたアニメーションをUnityに取り込むときのTips

Blender 作られたアニメーションを Unity に取り込む時に色々詰まったので、
そのときに知ってたらよかったなと思ったメモです。

# 前提
* Unity 2020.3.2f1
* Blender 2.9.2

# メモ
## fbx から Animation Clip を取り出す
知っていれば簡単。
1. Unityにfbxをドラッグ&ドロップでインポート
1. fbx を開いて見える Animation Clip を選択
![ARHideAndSeek_-_sb-idle-shooting-2_-_iOS_-_Unity_2019_4_22f1__PREVIEW_PACKAGES_IN_USE___Metal_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506075/37979fb0-ab59-879a-acc6-18c4da8be605.png)
1. Macなら `command + d` 、Windowsなら `ctrl + d` で取り出せる

## Animation Clip を取り出すときの注意
### ボーンの設定
Animation Clip を取り出す前に、ボーンの設定変更しておかないと Animation Clip が正常に動作しない可能性があります。例えば Humanoid 用の Animation Clip なのに、違うボーン設定のまま取り出すと、Humanoid に適用しても正常にアニメーションしません。

### アニメーションがループするときに、オブジェクトが時計回りに回転する
Animation Clip の Root Transform Rotation を設定すると治る可能性があります。[【Unity】Blenderで作成したアニメーションのループをなめらかにする方法](https://xr-hub.com/archives/12520)の　`アニメーションをループ再生させる` を参照ください。

### ０フレーム目にT字ポーズが入っている
Blender から Unity に取り込むために、0フレーム目をT字ポーズさせています。その場合は、取り出す前に1フレーム目から始まるようにしておくことで、T字ポーズを無視することができます。[【Unity】Blenderで作成したアニメーションのループをなめらかにする方法](https://xr-hub.com/archives/12520) の　`フレーム数調整` を参照ください。

### 1フレーム目から始めたが、アニメーション開示にカクツク場合
* clipsの一番上のアニメーションに0フレームにTポーズが入っていない可能性があるので、入っていなければ入れてください
* clipsの一番上のアニメーション初期位置が他のアニメーションに反映するようです
* clipsの一番上以外のニメーションは0フーレムにTポーズを入れなくても大丈夫のようです
Microsoft PowerBI Desktopで、飛び飛びな月日データのグラフ表示

記載日：2022/3/18
PowerBI Desktopで、不連続な月日データのグラフを作った時に手間取ったので、備忘録として記載します。

### 目標
不連続な月日データを使ってグラフを作る。

### つまづいた点
グラフx軸を日付表示したグラフを作りたいのに、日付表示されなかった。
月日を扱う場合、以下の条件を満たす必要があるらしい。
・月日が一意であること。
・連続した月日であること。（データの途中に月日の欠測がないこと。）

### 解決策
本データと別に、カレンダーのテーブルを用意することでうまく表示できた。

### 作業 (10分)
#### ０．本データのみのグラフ表示 (2分)
　　以下のようなデータ”入荷量.csv”を使ってPowerBIの折れ線グラフを表示させる。
　　グラフX軸には2/27, 2/28, 3/2, 3/3と表示されるのを期待したが、0～30となってしまう。
~~~
date,type,number
2022-02-27,みかん,16
2022-02-27,ぶどう,5
2022-02-27,いちご,6
2022-02-28,みかん,17
2022-02-28,ぶどう,15
2022-02-28,いちご,7
2022-03-02,みかん,19
2022-03-02,ぶどう,18
2022-03-02,いちご,6
2022-03-03,みかん,20
2022-03-03,ぶどう,9
2022-03-03,いちご,5
~~~
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1381309/8c945cc8-ff13-98b0-7685-2991de62bfac.png" width=80%>

#### １．カレンダーテーブルの作成 (3分)
　　”モデリング”の”新しいテーブル”を選ぶ。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1381309/bc9fcff5-cef5-014f-bfd6-8a6b3829f6d6.png" width=50%>
　　以下のように、CALENDER関数を使ったクエリを記述する。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1381309/dea30d79-1cf6-6097-1cc6-ecf50445f5a6.png)
　　カレンダーのテーブルが作成される。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1381309/8e457205-b34e-de1b-fe1c-4d07815d2b20.png" width=20%>

#### ２．カレンダーテーブルと本データの紐づけ (5分)
　　”テーブルツール”の”リレーションシップの管理”を選ぶ。”新規”ボタンを押す。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1381309/f5a401f4-8cbe-5c77-cbfe-b844eada1569.png" width=60%>
　　１つめのセレクタでカレンダーテーブルを、２つ目のセレクタで本データのテーブルを選択する。
　　カレンダーテーブルのDateカラムをクリックすると、自動的にカーディナリティなどが設定される。
　　OKを押す。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1381309/9959fc4e-7077-1c84-09e8-b415b3ac1d62.png" width=60%>

　　グラフを確認する。期待したグラフ（X軸が月日）が表示される。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1381309/5affbd4c-5c0f-ee8f-799d-f78c306cc7ea.png" width=60%>

### 参考にさせて頂いたサイト
・カレンダーテーブルを作る（カレンダーテーブルが必要）
https://frogwell.co.jp/blogs/powerbicalendartable/
・日付が一意かつ連続である必要がある
https://docs.microsoft.com/ja-jp/power-bi/transform-model/desktop-date-tables
ibatisでThere is no WRITEABLE property named ''xx" in class xxのエラー

最近ずっとStrutsでiBatisを使ったプログラムの改修をしていて、このエラーに遭遇したのですが、地味にググってもちょっと解決に詰まったのでメモ書き。

エラーメッセージは以下のような形式で出ていました。
```
com.ibatis.common.beans.ProbeException: There is no WRITEABLE property named 'xxx列名' in class 'パッケージ名.クラス名'
```

で、原因はというとほぼ[このブログ]("http://blog.livedoor.jp/ron_lou/archives/50812489.html")で書かれていた内容ではあったのですが地味に違っていて、なんとカラム名にアンダースコアが入っているときはgetterとsetterにも同様にアンスコを入れないと認識されない、でした。


例えば今回の場合、取得したいDBテーブルのカラム名が`prev_ansids`で取得用のDtoでは以下のような変数名にセットするようにしていた場合、以下のようなsetterとgetter名にするとこのエラーが出ます：

```AnswerDto.java
private String prev_ansIds;

public String getPrevAnsIds() {
     return prev_ansIds;
}

public void setPrevAnsIds(String prev_ansIds) {
      this.prev_ansIds = prev_ansIds;
}
```

これをちゃんと以下のようなコードに直したらエラーは出なくなりました。

```AnswerDto.java
private String prev_ansIds;

public String getPrev_ansIds() {
     return prev_ansIds;
}

public void setPrev_ansIds(String prev_ansIds) {
      this.prev_ansIds = prev_ansIds;
}
```
クエリを修正できない環境でOracleの性能改善のためにやったこと

## この記事について
グループウェアのDBにOracleを利用している環境でパフォーマンス問題が発生。キャッシュ追い出しでディスク読み込みが頻発していたことが原因の一つと判明したが、**パッケージ製品だからクエリ改善できない**、かつ、**データもすぐには減らせない**という場面に遭遇したので、その時の対応内容を記録してます。  
　※ 余裕があったら原因特定編や監視強化編も書きたい。

## 環境
- DB：Amazon RDS for Oracle 12.1.0.2.v21　
- Web周りは今回の主題では無いので詳細は省きます

## 改善施策

### Oracle
- マルチ・バッファプールの設定　
    - 利用頻度が高いテーブルをバッファに残す、あるいは、バッファ追い出しの原因となるテーブル（利用頻度が低くくフル・スキャンされるようなもの)を長時間保持しないようにそれぞれ指定可能。注意点は、どちらの指定にも容量があり、容量を超えると性能劣化を引き起こすため十分なサイジングが必要
    - 参考資料
        - [津島博士のパフォーマンス講座　 第13回 キャッシュ周りについて](https://blogs.oracle.com/otnjp/post/tsushima-hakushi-13)
        - [データベース・パフォーマンス・チューニング・ガイド](https://docs.oracle.com/cd/E82638_01/tgdba/tuning-database-buffer-cache.html#GUID-F2D11BC8-986F-4A8B-A37F-530BE9EB2DA8) 

### AWS
- IOPSが異常に高かったので、ストレージタイプを プロビジョンド IOPS SSD (io1) に変更
    - 良いところはオンライン変更できる点だが、劇的に改善はしなかった。状況を少しでも改善したい場合に試すのはアリかと。 
- キャッシュ追い出しが発生していたので、発生しないくらいのメモリのインスタンスクラスに変更　（ db.c5.4xlarge -> db.r5.4xlarge )
    - 変更時に再起動が発生するのが難点ではあるが、**これが一番効果的**だったと思われる。
BluePrint CustomEventでのReplicates


# Run On Serverが動作しない
CustomEventはClient→Server(Run On Server)は動作しない
>LogNet: Warning: UNetDriver::ProcessRemoteFunction: No owning connection for actor BP_AAA. Function OnCustomEvent will not be processed.

というエラーが表示される
CharacterのKeyEvent時点でRun On ServerでReplicateさせる

## Run On Server
オーナークライアント→Server

## Run On Owning Client
Server→オーナークライアント

## Multicast
ClientすべてとServer
numpyの...(Ellipsis)の使い方メモ

# 概要

numpyを使用するコードで偶に出てくる`X[..., n]`...
検索してもなかなかHitしない...
自分用にメモする

# ...の名称

pythonの機能でEllipisという。
日本語では”省略記号”。

https://docs.python.org/ja/3/library/constants.html

# numpyでの使い方

## 配列を省略して記載する

```python
# 確認用の配列。2進数っぽく値を取得。
# ex.
#   X[0,0,0,0] = 0
#   X[1,0,1,0] = 10
X = np.arange(0,16).reshape(2,2,2,2)

# 後方を省略
X[1,0,1,...] # <- array([10, 11])
X[0,...] # <- array([[[0, 1],[2, 3]],[[4, 5],[6, 7]]])

# 前方を省略
X[...,0,0,0] # <- array([0, 8])
X[...,1] # <- array([[[ 1,  3],[ 5,  7]],[[ 9, 11],[13, 15]]])

# 中間を省略
X[1,...,0] # <- array([[ 8, 10],[12, 14]])
```

## 配列の軸を追加する

Noneと共に使用して、単位軸を追加する。

```python
Y = np.array([1,2,3,4,5])

# 配列の形状を[5]から[5,1]に変更
Y[..., None].shape # <- (5, 1)

# 配列の形状を[5]から[5,1]に変更
# 見かけたことは無い！
Y[None, ...].shape # <- (1, 5)
```

以上。
Jiraでフィルターが削除できない

jiraでフィルターが削除できないことがあった。

- 原因
プロジェクトがゴミ箱に入っているため、ボードが見えない状態となり、関連するフィルターが削除できない状態になっていた。

- 対応
右上[設定] > [プロジェクト] > [ゴミ箱] より対象のプロジェクトを選択し、右側の [...]より [完全に削除]を実施。
Problem 99: 最大の指数

* 本記事は[ProjectEuler](https://projecteuler.net/about)の「100番以下の問題の説明は記載可能」という規定に基づいて回答のヒントが書かれていますので、自分である程度考えてみてから読まれることをお勧めします。

### 問題 99. 最大の指数

原文 [Problem 99: Largest exponential](https://projecteuler.net/problem=99)

**問題の要約：ファイルから$b^e$のbとeのペアを読んで$b^e$が最大になるのは何番目か答えよ**

例として以下のような大きな数の比較があげれらています。

```math 
\large 632382^{518061} > 519432^{525806} 
```


このような大きい数を扱うときには対数を使うのが常套手段ですね。以下の基本公式を使って比較します。今回は最大値のindexだけ分かれば良いのでnumpyの**argmax**を使いました。

```math 
\large log_{10} b^e = e \times log_{10} b
```

```python 
import numpy as np
import math
from google.colab import files
uploaded = files.upload()
#---- base/exp pair from file
bePair = list(map(lambda l: l.strip().split(","),open("p099_base_exp.txt")))
print(bePair)
print(f"Answer: {np.argmax([int(en)*math.log10(int(bn)) for bn, en in bePair])+1}")
```

（開発環境：Google Colab）
Kotlin KoansでKotlin入門　第１３回：Extension functions

# はじめに
公式の問題集[「Kotlin Koans」](https://play.kotlinlang.org/koans/overview)を解きながらKotlinを学習します。

過去記事はこちら
- Introduction
  - [第０回：Kotlin Koansについて](https://qiita.com/kosuke1/items/41d192af63485bfc8cb8)
  - [第１回：Simple Functions](https://qiita.com/kosuke1/items/5862a3ece96082a62e67)
  - [第２回：Named arguments](https://qiita.com/kosuke1/items/2af11262852ee41b5da7)
  - [第３回：Default arguments](https://qiita.com/kosuke1/items/16cb8ac780df58dc8054)
  - [第４回：Triple-quoted strings](https://qiita.com/kosuke1/items/52cfeed2c49b0252d40e)
  - [第５回：String templates](https://qiita.com/kosuke1/items/0221bbbc5bc70847be6f)
  - [第６回：Nullable types](https://qiita.com/kosuke1/items/aa96e422c5313b6961bc)
  - [第７回：Nothing type](https://qiita.com/kosuke1/items/1ca0e48820d58c9dfe16)
  - [第８回：Lambdas](https://qiita.com/kosuke1/items/8de74f9491e003127f53)
- Classes
  - [第９回：Data classes](https://qiita.com/kosuke1/items/1be2de1b4406b8d914bf) 
  - [第１０回：Smart casts](https://qiita.com/kosuke1/items/50bdecaa0740eb0e1a17)
  - [第１１回：Sealed classes](https://qiita.com/kosuke1/items/71a4da02fe1e99da5d63)
  - [第１２回：Rename on import](https://qiita.com/kosuke1/items/f11465cc15a94e7589a3)


# 問題
[Extension functions](https://play.kotlinlang.org/koans/Classes/Extension%20functions/Task.kt)

[extension functions](https://kotlinlang.org/docs/extensions.html?&_ga=2.187237800.1801786368.1647520462-1976109019.1644486873#extension-functions)について学びます。そして、extension functions```Int.r()```と```Pair.r()```を実装して、```Int```と```Pair```を```RationalNumber```に変換するようにします。

```Pair```は標準ライブラリで定義されているクラスです
```Kotlin
data class Pair<out A, out B>(
    val first: A,
    val second: B
)
```


修正前のコード
```Kotlin
fun Int.r(): RationalNumber = TODO()

fun Pair<Int, Int>.r(): RationalNumber = TODO()

data class RationalNumber(val numerator: Int, val denominator: Int)
```


# 問題のポイント
拡張関数を宣言するには レシーバタイプ (receiver type) を関数名の前に付ける必要があります。 次の例では、 swap 関数を MutableList<Int> に追加しています：
```Kotlin
fun MutableList<Int>.swap(index1: Int, index2: Int) {
  val tmp = this[index1] // 'this' がリストに対応する
  this[index1] = this[index2]
  this[index2] = tmp
}
```

拡張関数内のthisキーワードは、レシーバーオブジェクト（ドットの前に渡されるオブジェクト）に対応します。このような関数は、任意の MutableList<Int> に対して呼び出すことができます。

```Kotlin
val list = mutableListOf(1, 2, 3)
list.swap(0, 2) // swap()' 内の 'this' は 'list' の値を保持します
```
この関数は、どのMutableList<T>に対しても意味があり、汎用的に作ることができます。
```Kotlin
fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
    val tmp = this[index1] // 'this' はリストに対応する
    this[index1] = this[index2]
    this[index2] = tmp
}
```


# 解答例
```Kotlin
fun Int.r(): RationalNumber = RationalNumber(this, 1)

fun Pair<Int, Int>.r(): RationalNumber = RationalNumber(first, second)

data class RationalNumber(val numerator: Int, val denominator: Int)
```
Pythonのデータクラス

# Pythonのdataclass
下のほうに書いたインスタンス変数とクラス変数の件が頭の中にありちょっと混乱したので整理します。
Python3.7？以降ではクラスを`@dataclass`でアノテーションすると、自動的に`___init__()`が追加されるそうです。
次のsample1.pyだと`___init__(self, a:int, b:str)`があるのと同じに扱われます。

~~~sample1.py
@dataclass
class Test:
	a:int
	b:str
	def print(self):
		print(self.a)
		print(self.b)

t = Test(1,'abc') #　⇒　t=Test()だとエラーになる。__init__()の引数が足りないため。
t.print() #　⇒　1 'abc'と出力される
~~~

# Pythonのクラス変数とインスタンス変数
Pythonのクラスの変数には
・クラス変数
・インスタンス変数
の2種類があります。

下記のsample.pyのようにclass定義の直下に`a:int`や`b:str`のように書いているのが「クラス変数」です。
クラス変数は全インスタンスで共通の値が保持されます。詳細は後述します。
~~~sample1.py
class Test:
	a:int
	b:str
	def printTest(self):
		print(self.a)
		print(self.b)
~~~

また、下記のように__init__()で初期化しているのが「インスタンス変数」です。インスタンス変数はクラスをインスタンス化した変数それぞれで保持します。

~~~sample2.py
class Test:
    def __init__(self, a:int, b:str):
        self.a = a
        self.b = b
    def printTest(self):
        print(str(self.a))
        print(self.b)
~~~

また、__init__()を使わずともインスタンス変数は定義可能で、以下のようにインスタンス.変数=値で設定ができます。

~~~sample3.py
class Test:
	def printTest(self):
		print(self.a)
		print(self.b)

t=Test()
t.a=987
t.b='QWERTY'
~~~

実際に値を設定して出力すると以下のような動きになります。クラス変数は全インスタンスに共通なのが分かると思います。
ということでクラス変数の変更は要注意です。

~~~sample4.py
class Test:
	a:int
	b:str
	def printTest(self):
		print(self.a)
		print(self.b)

Test.a=10
Test.b='xyz'

t1 = Test()
t1.printTest() #　⇒　10 'xyz'と出力される（クラス変数）

t1.a = 20
t1.b = 'def' 
t1.printTest() #　⇒　20 'def'と出力される（インスタンス変数が優先される）

t2 = Test()
t2.printTest() #　⇒　10 'xyz'と出力される（クラス変数）
~~~

# 参考
[https://docs.python.org/ja/3.10/library/dataclasses.html](https://docs.python.org/ja/3.10/library/dataclasses.html)
PostgreSQLのBeginTextImportが失敗する


## 問題
PostgreSQLの、Npgsqlの、`BeginTextImport`の処理で、`Npgsql.PostgresException`の例外が発生する場合があります。その場合、データベースにも、データをインポートできません。

## 原因
`NpgsqlConnection`を、すぐに`Close`すると、例外が発生することがあるようです（しない場合もある）。

## 解決法
Closeしないでおけばインポートに成功します。ただし、Closeしないままで良いのかどうかわかりません。

## コード例
例えば、**クライアント側**で、CSVファイルから、データベースにインポートするのは、次のコードになります。
`conn.Open()`するので、`conn.Close()`したわけですが、これだと失敗する場合があります。

``` csharp
var connString = "Host=localhost; Port=5432; Username=postgres; Database=HOGEDatabase;";
using var conn = new NpgsqlConnection(connString);
conn.Open();
using var writer = conn.BeginTextImport(@"COPY tablename (field_text, field_int4) FROM STDIN (FORMAT CSV , HEADER)");
using StreamReader csvReader = new(@"C:\test.csv", Encoding.UTF8);
string? str;
while ((str = csvReader.ReadLine()) != null)
{
   writer.WriteLine(str);
}
//conn.Close();  //ここで例外が発生することがある
```

### 備考
- 当初、テーブルをCSVファイルにエクスポート・インポートする処理を、EntityFramework Core と、CSV Helper で行おうとしました。しかし、`jsonb`の列の処理が、大変そうだなと思われたので（検証していないので、実際はどうかはわかりません）、別の方法を探し、PostgreSQLのCOPYを利用するのが良さそうだと分かりました。
- このCOPYは、サーバー側と、クライアント側とで、使い方が違うので、混乱しますね。

2022-03-19追記。
- CSV Helperの[Type Conversion](https://joshclose.github.io/CsvHelper/examples/configuration/class-maps/type-conversion/)を利用すれば、クラスをJsonにしCSVに保存（またその逆）も問題ないことがわかり、CSV Helperを利用してインポート・エクスポートの仕組みにしました。
- PostgreSQLのCOPYは、idが一致する時置き換える仕組みがないようなので、利用を断念。
- クラスをJsonbにした時に、Jsonbに保存されるJsonの文字列を直接取り出す方法が見つからなかったので、これも断念。結局、Jsonb →　EntityFrameworkCoreのクラス　→ CSVHelperでJsonにタイプコンバート → CSVという方法を採用しました。CSVからJsonbへはこの逆です。

### 参照
- [Npgsql Bulk copy](https://www.npgsql.org/doc/copy.html)

### 環境
- Npgsql 7.0.0-preview.1
- .NET 6
Microsoftのデータサイエンス初心者向けコースをやってみるDay1チャレンジ：テキストマイニング

# はじめに

　GAFAM(Google, Amazon, Facebook, Apple, Microsoft)のMは何と言ってもMicrosoft。
ビルゲイツ氏の時代はOSのイメージが強かったが、サティアナデラ氏がCEOの現在は、クラウドでイケイケの会社となっており、超ハイスキルの人財を多数抱え、世界最先端で人工知能・データサイエンス・機械学習を学べるコースを無償で提供している。そこで、Microsoftの寛大さに感謝しつつ、Pythonを使いながら、データサイエンスを勉強してみる。

# 対象読者

　Pythonとデータサイエンスに興味があって、英語が苦手な人（英語が得意な人は、[参考文献](#参考文献)を直接解いてください。

# ロードマップ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409610/d082f507-0ac6-daa3-123b-3b36b42c6e66.png)


https://github.com/microsoft/Data-Science-For-Beginners/blob/main/sketchnotes/00-Roadmap.png?raw=true

#  Day1

この記事では、Day1のチャレンジを翻訳しながらやってみる。


## Day1ゴール

データサイエンスに関するWikipediaの記事からキーワード集計や可視化を行い、
気づき・洞察を得る。

## Wikipediaからデータサイエンス記事を取得する。

```Python
import requests

url = 'https://en.wikipedia.org/wiki/Data_science'
ds_text = requests.get(url).content.decode('utf-8')
print(ds_text[:2000])

```

## データ変換（HTMLから平文のテキストへ）

　HTMLParserクラスでHTMLタグ内のテキストが取得できるが、子クラスのMyHTMLParserではscriptタグと、styleタグは読み飛ばす処理を入れている。

```Python

from html.parser import HTMLParser

class MyHTMLParser(HTMLParser):
    script = False
    res = ""
    def handle_starttag(self, tag, attrs):
        if tag.lower() in ["script","style"]:
            self.script = True
    def handle_endtag(self, tag):
        if tag.lower() in ["script","style"]:
            self.script = False
    def handle_data(self, data):
        if str.strip(data)=="" or self.script:
            return
        self.res += ' '+data.replace('[ edit ]','')

parser = MyHTMLParser()
parser.feed(ds_text)
ds_text = parser.res
print(ds_text[:2000])
```


## 高速頻度集計にnlp-rakeモジュールを使う。

https://pypi.org/project/nlp-rake/

```shell

import sys
!{sys.executable} -m pip install nlp_rake

```



## 頻度集計

5文字以上で、3回以上出現した２つの複合語までの単語を取得。

```Python

import nlp_rake
extractor = nlp_rake.Rake(max_words=2,min_freq=3,min_chars=5)
res = extractor.apply(ds_text)
print(res[:5])
```
[('machine learning', 4.0), ('big data', 4.0), ('data scientist', 4.0), ('21st century', 4.0), ('data science', 3.9107142857142856)]

## matplotlibで可視化

```Python

import matplotlib.pyplot as plt

def plot(pair_list):
    k,v = zip(*pair_list)
    plt.bar(range(len(k)),v)
    plt.xticks(range(len(k)),k,rotation='vertical')
    plt.show()

plot(res)

```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409610/33b6722b-f990-af80-51fd-dd6a87d2bd90.png)


## WordCloud（ワードクラウド）で可視化

```Shell

!{sys.executable} -m pip install wordcloud

```

```Python

from wordcloud import WordCloud
import matplotlib.pyplot as plt

wc = WordCloud(background_color='white',width=800,height=600)
plt.figure(figsize=(15,7))
plt.imshow(wc.generate_from_frequencies({ k:v for k,v in res }))

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409610/d44bb99a-9b80-b09d-0b64-3b4ea37e774a.png)


　おしゃれなワードクラウドの出来上がり。さあ召し上がれ。


# 著者のTWITTERアカウント

@keiji_dl

# ホームページ（pytorch/python/nlp）

https://deeplearning.sakura.ne.jp/hp


# 参考文献

https://github.com/microsoft/Data-Science-For-Beginners

https://github.com/microsoft/Data-Science-For-Beginners/raw/main/sketchnotes/01-Definitions.png

https://www.youtube.com/watch?v=beZ7Mb_oz9I
ディレクトリ構造チックにネストした辞書を作れるPathDictを見つけた話

 これは備忘録です。 
 何かPythonでオブジェクトを管理する処理を書きたいときに、ディレクトリ構造みたいに配置できたらなと思うことが往々にしてあります。
  なにかいいライブラリないかなーと探していると3秒で出てきました。**Python dict path like** で出てきました。
- [PyPI](https://pypi.org/project/path-dict/)
- [GitHub](https://github.com/mkrd/PathDict)

さてインストールして使ってみますか。

```shell
pip install path-dict
```
#### インポート

```python
from path_dict import PathDict
pd = PathDict()
```

#### 使ってみる

```py
user = {
	"name": "Joe",
	"age": 22,
	"hobbies": ["Playing football", "Podcasts"],
	"friends": {
		"Sue": {"age": 30},
		"Ben": {"age": 35},
	}
}
joe = PathDict(user)

print(joe["name"])
# joe
print(joe["friends","Sue"]) # 検索先がフォルダだとPathDictになって返す
# PathDict({
#   "age": 30
# })

```

事前に親ディレクトリまでのパスを作らなくても作ってくれる

```py
pd = PathDict()
pd["a", "b", "c"] = 1
print(pd["a", "b", "c"])
# 1

print(pd)
# PathDict({
#   "a": {
#     "b": {
#       "c": 1
#     }
#   }
# })
```

# 詰まったこと
基本的な使い方に関しては[公式ドキュメント](https://pypi.org/project/path-dict/)が非常にわかりやすく、```フィルター機能``` も便利だが肝心なところで躓いたのでメモ。

### ファイルパスを利用して登録したい。
~~パスの文字列をsplitして入れてみるとエラー~~
(2022/3/26 追記) 本家にpull requestが通りましたのでtuple型に変換しなくも処理できるようになります。 
```py
from path_dict import PathDict
pd = PathDict()
path = "path/to/file"
obj = 10
pd[path.split("/")] = obj
# TypeError: unhashable type: 'list'
```

どうやら公式実装の **__ getitem __** メソッドがtuple型しか受け入れないようです。

```py:path_dict/__init__.py
...
	def __getitem__(self, path) -> Any | PathDict:
		""" Subscript for <PathDict>.get_path() """
		# If PathDict["key1"], then path="key1"
		# PathDict["key1", "key2"], then path=tuple("key1", "key2")
		# We want path to be a list in any case
		path = list(path) if isinstance(path, tuple) else [path]
		return self.get_path(path)
...
```

ですからこのようにしていちいちtupleに型変換しないといけません。

```py
from path_dict import PathDict
pd = PathDict()
path = "path/to/file"
obj = 10
pd[tuple(path.split("/"))] = obj # OK
```

ちょっと面倒くさいですね。修正してプルリクエストしておきます。


# 注意事項
個人的にこれは気を付けないと危険だと思った仕様
  
**存在しないPathを指定してもエラーを吐かない**

存在しないパスを指定すると *None* を返します。

```py
from path_dict import PathDict
pd = PathDict()
print(pd["a","b"])
# None
```

つまり事実上None型をValueとして登録できません。しかしここでエラーを吐いてしまうと辞書がそのパスを持っているか調べるメソッドがありません。Issue案件です。
...
(2022/3/26 追記) `__contains__` メソッドの実装をしプルリクエストを本家に送りました。pathの有効性のチェックが可能になります。

```py
from path_dict import PathDIct
pd = PathDict()
pd["a","b"] = 1
print(["a"] in pd) # True
print(["a","b"] in pd) # True
print(["a","c"] in pd) # False
```

# まとめ

- Pythonの内部でディレクトリ構造みたいにオブジェクトを登録できるライブラリを見つけたよ。
- tuple以外受け容れない問題があるよ
- キーが存在しないときは *None* を返しエラーを吐かないので気を付けてね。

Issueとプルリクエストしよ。
S3における暗号化の種類

# 調べる前のイメージ
色々なオプションの暗号化があるなーっていうイメージ。
それぞれの詳しい内容は分からない。

# S3におけるデータ暗号化
S3に保管されたデータはさまざまな方式で暗号化が可能。
キー(鍵)の管理については、クライアント(ユーザー)が独自に作成したキーを持ち込む、またはAWS KMSによる管理やS3がデフォルトで提供するキーを使うなど、いくつかのオプションがある。

SSE＝サーバーサイド暗号化
CSE＝クライアントサイド暗号化
# SSE-S3
S3にて提供・管理される暗号化キー(AES-256)で暗号化する。

# SSE-KMS
AWS Key Management Service (AWS KMS) にて提供・管理される。

# SSE-C
クライアント(ユーザー)が作成・管理する独自の暗号化キーで暗号化する。

# CSE
S3にデータを送る前にクライアント(ユーザー)側で暗号化する方法。
数値型1234を4321といった並び替えするにはどうすればよいか

# 概要
書いてある通り。
何桁かの数字列があたえられたときに
並べ替えたりある桁とある桁を入れ替えるなどの操作をするときに数値型だと大変なので
一度ベクタに直しているがこれが大変に面倒
そこで何とかならんか試した

## 素朴に試した
戦略としては
数値型⇒string⇒vec<char>⇒ここで並び替え⇒string⇒数値型
である。
https://qiita.com/kujirahand/items/fcb4f75dbdbfaf36aa75
https://qiita.com/smicle/items/29a4d5d1d14ad7f77f60
を参考にした

```rust:main.rs
fn main() {
let num:usize =1234;
  let stri: String = num.to_string();
  let mut cs: Vec<char> = stri.chars().collect();
  cs.sort_by(|a, b| b.cmp(a));
  let st: String = cs.iter().collect();
  let numm:  usize = st.parse().unwrap();
  println!("{}",numm)
}
```

## 素朴がすぎる
書いてある通り。
なんとかしたいなと。思いつくのは↓だがうーんという感じ

```rust:main2.rs
fn main() {
        let mut n = 1234;
        let mut a = vec![];
        while n > 0 {
            a.push(n % 10);
            n /= 10;
        }
        a.sort();
        let mut y = 0;
        for a in a.iter().rev() {
            y = 10 * y + *a;
        }
    println!("{}", y);
}
```
## 短くしたいが。。。
短くしただけになってしまった


```rust:main.rs
fn main() {
    let mut n = 1234;
    let mut cs:Vec<char> = n.to_string().chars().collect();
    cs.sort_by(|a, b| b.cmp(a));
  let numm:  usize = cs.iter().collect::<String>().parse().unwrap();
    println!("{:?}", numm);
}
```
小ネタ／Aurora MySQL v1/v2 から v3 に移行する際のユーザ権限トラブルについて

AWS の Aurora MySQL v1（MySQL 5.6 互換）の EoL 発表によって、v2（同 5.7 互換）または v3（同 8.0 互換）への移行を計画している方もいらっしゃるのでは？と思います。

というわけで、わたしも Zenn のほうに記事をいくつか書きながら調査を進めております。

https://zenn.dev/hmatsu47/articles/aurora-mysql3-001-top

https://zenn.dev/hmatsu47/articles/aurora-mysql3-002-ref-material

https://zenn.dev/hmatsu47/articles/aurora-mysql3-003-ref-aws-01

https://zenn.dev/hmatsu47/articles/aurora-mysql3-004-ref-aws-02

https://zenn.dev/hmatsu47/articles/aurora-mysql3-005-ref-ora-01

https://zenn.dev/hmatsu47/articles/aurora-mysql3-006-research-01

https://zenn.dev/hmatsu47/articles/aurora-mysql3-007-research-02

※Zenn に書いている理由 : 一連の記事を GitHub 管理したかったから

調査を進める中でテスト環境を立て、スナップショット復元で v1 → v2 → v3 移行する中でタイトルの問題点に引っかかったので、備忘として残しておきます。

なお、タイトルには Aurora MySQL と書きましたが、本家 MySQL でも発生しうる問題（のはず）です。

# 発生した問題その 1 : 管理者権限が移行されない

v3 移行後のサーバに MySQL Workbench で接続…はできたのですが、ユーザの一覧も表示されなければスキーマ（DB）もテーブルも何も見えない状態になりました。

## 原因

https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-23.html#mysqld-8-0-23-connection-management

> Selection of the account that matches incoming TCP client connections could be affected by account creation order. To make the matching algorithm more deterministic, matching the host name part of accounts now checks accounts specified using host IP addresses, in a specific order, before attempting to match accounts specified using host names. Host name matching remains unchanged. See Access Control, Stage 1: Connection Verification.

https://dev.mysql.com/doc/refman/8.0/ja/connection-access.html

> MySQL 8.0.23 より前は、リテラル IP アドレスの特異性はネットマスクがあるかどうかの影響を受けないため、198.51.100.13 と 198.51.100.0/255.255.255.0 は同等とみなされます。 MySQL 8.0.23 の時点では、ホスト部分に IP アドレスを持つアカウントには次のような特異性があります:
（後略）

実は、

- `'【ユーザ名】'@'%'`
  - Aurora クラスタ／インスタンス作成時に自動生成されたユーザアカウント

と同じユーザ名の

- `'【ユーザ名】'@'【接続元 IP アドレス範囲】'`

を手動で作って、こちらに前者と同じ権限を付与して運用していたのですが、v1 → v2 スナップショット復元では後者のユーザに管理者として必要な権限が移行されませんでした（メジャーバージョン移行時の**仕様**であり、不具合ではないと思います）。

それでも**v2 時点では`'【ユーザ名】'@'%'`に指定された権限が優先**され、MySQL Workbench ではユーザの操作もスキーマ・テーブルの操作もできました。

ところが前述のとおり **MySQL 8.0.23 でユーザの評価ルールが変わり、接続元（アカウント名）の IP アドレス（ホスト）の特定度が高いものが優先されるようになった**ため、管理者権限が移行されなかった **`'【ユーザ名】'@'【接続元 IP アドレス範囲】'`のほうで接続している**とみなされた、というわけです。

## 対処

- **指定の IP 範囲外のクライアントからサーバに接続して`'【ユーザ名】'@'【接続元 IP アドレス範囲】'`を削除**
- **それが無理ならあらためて v2 に戻って`'【ユーザ名】'@'【接続元 IP アドレス範囲】'`を削除してからスナップショット取得→ v3 で復元**


のいずれかです。

# 発生した問題その 2 : 一部のユーザの権限が無効になる（アクセスが拒否される）

Reader インスタンスへのアクセスで`SELECT`権限だけ付与していたユーザのアクセスが、v2 までは問題がなかったのに v3 で拒否されるようになってしまいました。

## 原因

**「権限付与対象スキーマとしてワイルドカード`'%'`（のみ）を指定すると権限が無効になる」** でした。

この指定は v2（MySQL 5.7）までは有効でした。

## 対処

- **`GRANT 【権限】 ON '%'.* TO 【アカウント名】`**

を

- **`GRANT 【権限】 ON *.* TO 【アカウント名】`**

に書き換えました。

ただ、本来であればワイルドカードで全てのスキーマに対して権限を付与するのは良くないので、個別のスキーマ名を指定して権限を付与すべきでしょう。

なお、↑のケースでは、`SELECT`ではなく更新系の権限を指定すると、`mysql`スキーマに対する更新系の権限を`REVOKE`する指定が自動的に生成されます。
【Android】Android 13から始める revokeOwnPermissionOnKill

# revokeOwnPermissionOnKill とは
Android13 DP2(Tiramisu)から追加された機能で、Runtime Permissionを意図的に削除させることができます。
ちなみに、DP1では[selfRevokePermission](https://developer.android.com/sdk/api_diff/t-dp2-incr/changes)と呼ばれていたのでStable時には変わっている可能性は大いにありそうです。


https://developer.android.com/reference/android/content/Context#revokeOwnPermissionsOnKill(java.util.Collection%3Cjava.lang.String%3E)

以下抄訳↓

- 1つ以上の権限を取り消すトリガー
- 取り消しは、非同期で行われる
- アプリがフォアグラウンドにいる間や、他のアプリがContentProviderにアクセスしている場合はトリガーは発火しない
- すぐ実行して欲しい場合は、 プロセスを終了させる

```Process.kt 
exitProcess(0)
```

# 実際に試してみる

#### 1. target sdkをTiramisuDP2以上に設定する

```gradle:build.gradle
android {
    compileSdkVersion "android-Tiramisu"

    defaultConfig {
        targetSdkVersion "Tiramisu"
        minSdkVersion "Tiramisu"
    }
}
```


#### 2. 使用するmoduleのManifestへ任意のpermissionを追加する

```xml:AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA" />
```

#### 3. permissionのrequestとrevoke処理を追加する

```kotlin:MainActivity.kt
companion object {
   // Permission
   private val PERMISSION_REQUIRED = Manifest.permission.CAMERA
}

private val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
    if (isGranted) {
       // granted
    } else {
       // not granted
    }
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
     super.onViewCreated(view, savedInstanceState)

     permitRequestBtn.setOnClickListener {
         if (ContextCompat.checkSelfPermission(context, PERMISSION_REQUIRED) != PackageManager.PERMISSION_GRANTED) {
              // request
              requestPermissionLauncher.launch(PERMISSION_REQUIRED)
         }
     }
     
     permitRevokeBtn.setOnClickListener {
         if (ContextCompat.checkSelfPermission(context, PERMISSION_REQUIRED) == PackageManager.PERMISSION_GRANTED) {
            // revoke
            context.revokeOwnPermissionOnKill(PERMISSION_REQUIRED)
         }
     }
}
```

#### 4. 試してみる
※わかりやすい例として、HOME'Sアプリ内かざして検索にてカメラを利用しているのでスクショを添付

1. Requestボタンでカメラの権限リクエスト
1. Revokeボタンで権限の取り消しリクエスト
1. アプリを一度キルして再度立ち上げる

|Request|Revoke|
|--|--|
|<img height="10%" alt="request.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223159/04271891-c185-16eb-4c24-25fff19b88b6.png">|<img height="30%" alt="revoke.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223159/c0310515-8718-2414-f48f-59a365768177.png">|


#### 最後に
単体だけでなく、複数権限を同時に取り消すことも可能でした。
また、取り消しリクエスト後もexitProcess(0)を呼ぶのは現実的ではないと思うので、ユーザーにアプリキルを促す体験など込みで考える必要がありそうです。
利用ケースとしては、特定の権限ありきで提供していた機能の削除時に呼んでおくといったところでしょうか。ユーザーと開発者の双方にとってメリットになる機能なので積極的に取り込んでいけると良さそうです。
laravelの基本のき【個人的なお勉強アウトプット】

# 参考図書
https://www.amazon.co.jp/dp/B07CLLW4MX/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1

## laravelのインストール
laravelはComposerからインストールする。
`composer create-project laravel/laravel laravelapp --prefer-dist`
インストールしたあと、インストールしたアプリケーションフォルダからターミナルで`php artisan serve`でアプリケーションを実行する。
実行し`http://localhost:8000`にアクセスするとlaravelの初期画面が表示される。
なぜ表示できるかというと、laravelはPHP本体にあるサーバ機能をつかって開発環境用のサーバーを立ち上げている。
ただし、動作確認用の簡易的なものであり、本格運用に耐え得るほど堅牢なサーバーではない。この機能をつかってアプリケーションを公開してはいけない。

## 公開パスの変更
laravelをそのまま公開すると、ルートパスは`http://ドメイン/laravelのプロジェクト名/public`となる。
`/`がルートパスになるよう、サーバー側で表示設定をする。

MAMPの場合は以下
MAMP/conf/apache/httpd.conf
```
Alias / "/Applications/MAMP/htdocs/laravelapp/public/"

<Directory "/Applications/MAMP/htdocs/laravelapp/public/">
    Options Indexes FollowSymLinks MultiViews
    AllowOverride all
    Order allow,deny
    Allow from all
</Directory>

```

## laravelプロジェクトでよく使うフォルダ
- app
- routes
- resources

appはもっとも重要かつ頻度が高い
プログラムを色々と作っていく場所
Httpフォルダの中に基本的なプログラムを作成していく
コントローラーもこの中に入れる
コントローラーは慣習的に`app/Controllers`として`Controllers`フォルダの中にまとめる

routesはルーティングを設定するところ
resourcesは表示するテンプレートを置くところ

## ルーティング
laravelはルーティングでアドレスと読み込み・表示される処理を紐付けている
ルーティングの設定はroutesフォルダの中で設定している
```
Route::get('hello', 'App\Http\Controllers\HelloController@index');
```
Routeクラスにgetメソッドを実行している。
第一引数にパス、第二引数に`コントローラー名@アクション`を設定。

## コントローラー内のアクション
アクションとは、コントローラーの中で定義する関数
例えば以下のindex
```
class HelloController extends Controller
{   
    public function index(){
        $data = ['one','two','three','four','five'];
        return view('hello.index', ['data'=>$data]);
    }
}
```

### 表示するテンプレートの指定
viewメソッドで表示するテンプレートを指定している。
第一引数で表示するテンプレートを指定。
上記の例だとテンプレートは以下
`/resources/views/hello/index`

### コントローラーからビューへの変数の渡し方
第二引数でテンプレートに変数を渡している
テンプレートでは配列のキーの名前が変数の名前として使える。
上記の例の場合は、`$data`として`$data`の配列が渡される。

```
public function inde($id='zero'){
$data = [
'msg' => 'こんにちは',
'id'=> $id
]
return view('hello.index', $data);
}
```
上記の例の場合は、テンプレートで`$msg`、と`$id`が使える。

## テンプレート
テンプレートは普通のPHPスクリプトファイルとして記述する方法と、laravelが用意しているBladeというテンプレートエンジンを使うことができる。
Bladeには効率的にレイアウトを作成していくための機能があるので使っていったほうがよい。
Bladeを使う場合はファイル名にindex.blade.phpとする。
index.phpと両方ある場合はindex.blade.phpが優先して読まれる。

### bladeの記法
bladeファイル内では変数は
`{{$msg}}`のように使う。
また、bladeにはデフォルトでいくつかの構文が用意されている。
@for、@foreach、@if、@isset、@unless、@empty
また、@loopというループ処理に関わる色々な情報を取り出せる構文もある。
Laravelのbladeテンプレートの作り方

# 概念
- レイアウトの親ファイルはresources/views/layoutsの中に入っている
- コンポートのようなパーツは、resources/viewsの中に直で入っている


# 親レイアウトの作り方
いつも通りHTMLを書いて、コンポーネントのようなパーツを挿入したいところに`@yield('パーツ名')`を挿入するだけ

# パーツの作り方
1. `@extends('layouts.親レイアウトの名前')`を最初に挿入して、どのレイアウトを継承するかを書く

2. `@section('パーツ名')`で書き出して、パーツの名前を指定。ここで使ったパーツ名を親レイアウトで使う

3. `@endsection`で書き終わり
Laravelでルーティング(web.php, コントローラー)の使い方

# web.phpに最初から入っているコードを使って解説

初めてlaravelのプロジェクトファイルを作って、laravel/uiをインストールすると、web.phpの最終行に以下の記述がある

```web.php
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
```

- `/home`: ドメインの後に入るファイルパスを指定
- `App\Http\Controllers\HomeController::class`: HomeControllerのclassメソッドを参照
- `->name('home');`: ルーティング名


# web.phpに新しいルートを作ってみる
web.phpの最終行に先ほどのコードを複製して、ファイルパスの部分と、ルーティング名を変更する(ややこしくなるので名称は同じにしてOK)

```web.php
Route::get('/任意の名前', [App\Http\Controllers\HomeController::class, 'index'])->name('任意の名前');
```


# コントローラーに追加
app/Http/HomeController.phpを開く。
すると、さっきのHomeControllerクラスを継承したControllerが入っている。

24行目〜27行目あたりにある以下の記述を見る。
以下の記述の部分で、**index**に来たら(この関数が呼ばれたら)、コントローラーはviews/**home.blade.php**を返す、と書いてある。

```HomeController.php
public function index()
    {
        return view('home');
    }
```
これをもとに新しいルートを作りたいので、下の行に複製する。


複製できたら、さきほどweb.phpで書き込んだ任意の名前を入力

```HomeController.php
public function 任意の名前()
    {
        return view('任意の名前');
    }
```
すると、**任意の名前**に来たら(この関数が呼ばれたら)、コントローラーはviews/**任意の名前.blade.php**を表示させる、という記述が完成する。
Schemaspy で ER 図を作成する

Schemaspy を用いて、既存のDBから ER 図を自動的に生成します。
https://schemaspy.org/

コンフィグレーションファイルを作成します。

```text:schemaspy.properties
schemaspy.t=pgsql
schemaspy.host=gateway.docker.internal
schemaspy.port=5432
schemaspy.db=<YOUR DATABASE>
schemaspy.u=<YOUR USERNAME>
schemaspy.p=<YOUR PASSWORD>
schemaspy.o=/output
````
DB は PostgreSQL が hocalhost:5432 で起動している前提です。 Schemaspy を Docker で起動させるので、ホストには `gateway.docker.internal` を設定しています。

http://docs.docker.jp/v19.03/docker-for-mac/networking.html#mac-i-want-to-connect-from-a-container-to-a-service-on-the-host

コンテナを実行します。

```
docker run -v "$PWD/docs:/output" -v "$PWD/schemaspy.properties:/schemaspy.properties" schemaspy/schemaspy:latest
```

`docs` 以下にファイルが生成されるので `index.html` を開きます。
MiniO バケットのアクセスポリシーの変更

# 概要

- MiniOのバケットのアクセスポリシーを変更する方法をまとめる。

# 方法

1. バケット一覧で「Manage」をクリックする。

    ![MinIO_Console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/1d795101-12a2-a609-e31b-e9cc876b1ec1.png)

1. SummaryタグのAccess Policyがデフォルトだと「Private」になっているはずなのでここをクリックする。

    ![MinIO_Console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/cd155541-9617-afd0-f936-9fe65e64426f.png)

1. 「Public」を選択して「Set」をクリックする。

    ![Notification_Center.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/62978ed9-1dcc-442a-2897-75ced623aa67.png)
MiniO バケットの作成

# 概要

- MiniOでバケットを作成する方法をまとめる。

# 方法

1. ブラウザからMiniOにアクセスしてログインする。（Dockerコンテナで起動している場合、docker-compose.ymlなどに認証情報を記載して起動する。）
1. 「Create Bucket」をクリックする。

    ![MinIO_Console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/71e5328c-7bdb-a186-06d3-47477123842c.png)

1. バケット名を入力し（今回は「local-bucket」とした）「Create Bucket」をクリックする。

    ![MinIO_Console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/379159f3-5c1d-fdcb-87de-be77ed381995.png)

1. バケットが作成される。

　　　　![MinIO_Console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/8daa5aad-fd5b-b3ec-2b4a-c2357aaac217.png)
deviseのログイン後、ログアウト後のリダイレクト先を設定する方法

# 環境

Ruby 3.0.2
Rails 7.0.2.3

# コード


```ruby: app/controllers/application_controller.rb
class ApplicationController < ActionController::Base

  private

  # ログイン後のリダイレクト先
  def after_sign_in_path_for(resource_or_scope)
    root_path  #ここを好きなパスに変更
  end

  # ログアウト後のリダイレクト先
  def after_sign_out_path_for(resource_or_scope)
    new_user_session_path #ここを好きなパスに変更
  end
end

```

これだけです。

# 余談

ログアウト後の遷移先は設定必要だと思いますが、
ログイン後の遷移先は、例のように `root_path`にするならば、そもそも `config/routes.rb`で設定してあるので不要だと思います。
```ruby: config/routes.rb
Rails.application.routes.draw do
  devise_for :users
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Defines the root path route ("/")
  root 'users#index'
end

```

# 参考
https://stackoverflow.com/questions/6514421/redirecting-devise-after-sign-out

https://github.com/heartcombo/devise/wiki/How-To%3A-Change-the-redirect-path-after-destroying-a-session-i.e.-signing-out
AWS Data Analytics - Specialty対策 in Redshift

## 概要
数百ギガバイトから1ペタバイト以上まで拡張が可能で、高速、シンプル、スケーラブルで費用対効果が高い完全マネージド型のデータウェアハウスサービスです
## 関連する用語

### データウェアハウス(DWH)
意志決定(Decision)のため、目的別(Purpose-oriented)に編成され、統合(Integrate)された時系列で、削除(Delete)や更新(Update)しないデータの集合体


## Redshiftの仕組み
![スクリーンショット 2022-03-18 18.30.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/eb1dc14f-2feb-2a2d-268f-2f8f5365b027.png)

### リーダーノード
- クライアントと直接通信を行う
- 課金対象外

### コンピューティングノード
- クエリを並列して実行する
- コードを実行して中間結果をリーダーノードに返却する

### ノードスライス
- ノード内のメモリ、ディスクを分割した論理的な処理単位
- インスタンスタイプによりスライス数は異なる(2~16)



## Redshiftの特徴

### クエリ速度が早い
- 他のクラウドサービスよりも圧倒的にクエリ速度が早い

### コストが低い
- オンプレミスと比較しても75%コストをカットできる
- リザーブドインスタンスの購入により更にコストカットができる

### 伸縮性
- 必要に応じてスケールアップやスケールアウトが容易に行える

## Redshiftのユースケース
- ダッシュボード
- アドホック分析
- ETL/パッチ
- 機械学習の前処理
- 巨大なデータセット
- 複雑だが同時実行数が少ないSQL
- データの更新は一括で実行

大容量データを高速に集計・分析する必要のあるワークロードに向いています。

## Amazon Redshift Spectrum
Redshiftの場合ストレージはRedshift管理下のストレージを使っていたがユーザが作成したS3を指定して直接クエリを行うことができる機能も存在する

## 試験でよくある問題対策

### TTLのサポート
- TTLのサポートはしていない

### ワークロード管理
- 複数の処理を別のキューを使うことで干渉を起きないようにできる

### 分散スタイル
- [こちら](https://www.youtube.com/watch?v=OY0LVdXTOXg)の動画の3:00 ~ 10:00がとても参考になります

### 大量データの更新時のクエリパフォーマンスの低下
- データの更新作業が入りバキューム処理が阻害されたため


## 内容については随時更新します
間違っているところなどありましたら是非コメントいただきたいです:bow

## 参考にさせていただいた記事
https://www.youtube.com/watch?v=OY0LVdXTOXg
https://macro-send.com/blog/redshift
https://dev.classmethod.jp/articles/relay_looking_back_redshift/
https://docs.aws.amazon.com/ja_jp/redshift/latest/dg/c_high_level_system_architecture.html
【備忘録】Android Studioで生成するAABを圧縮する手順(R8)

# 概要
Androidアプリを開発していると、いつの間にか生成するAABのサイズが肥大化していきます。
AABが大きいということは、AABをスマートフォンにダウンロードするのも大変なわけで、なるべく小さくしたいと考えるわけです。
Androidでは、「圧縮」をすると同時に難読化なども行うことができます。
ここでは、その設定を備忘録として残してます。

# R8 コンパイラ
Androidデベロッパー向けに「圧縮、難読化、最適化」の設定が提供されています。

* アプリの圧縮、難読化、最適化
https://developer.android.com/studio/build/shrink-code?hl=ja

# 手順
手順は、至って簡単です。

まずは、build.gradle（app側）の中から、下記の設定を見つけます。
```build.gradle(app)
buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}
```

見つけたら、その部分を下記に書き換えます。
具体的には、「minify」を有効にする。「shrinkResources」を追加し有効にする。以上です。
```build.gradle(app)
buildTypes {
    release {
        minifyEnabled true
        shrinkResources true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}
```

# 効果
単純にどれだけ小さくなるか？はものに寄りけりですが、この設定によって、GooglePlayConsole上でかなりの恩恵を受けます。

例えば、「基本アクティビティ」でサンプルのプロジェクトを作り、何も変更せずにビルドしてAABを作成すると「3,852KB」でした。
一方、R8コンパイラの設定に書き換えると「1,743KB」になりました。
半分以下のサイズになった・・・・

GooglePlayConsole側もアプリサイズの肥大化を気にしているようですし、小さくするのが良いですね。
![CAB001.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222251/352d9e8a-45a2-77d4-0292-a1ae531cddfb.png)

https://support.google.com/googleplay/android-developer/answer/9859348?hl=ja
ラズパイで最初にすることまとめ

# 0.前提

私がRasberry Piを買って、最初におこなった作業を書きました。
Devbian系のディストロには応用できるかと思います。

- ユーザの作成
- 日本語化
- タイムゾーンの設定
- インターネット接続

などは済ませたものとします。

# 1.SSHの設定

なんといっても、一番はこれ。
自分のよく使うマシンから、`ssh-copy-id`コマンドで、SSHの公開鍵を登録します。
このコマンド便利すぎて初めて知ったときは驚愕したね。
とりあえず、このスーパー便利なコマンドを使って、キーを登録しましょう。

## IPアドレスの確認
新マシンのIPアドレスが必要になってくるので、調べます。

```bash:新マシンで実行するコマンド
ifconfig
```

もしくは

```
ip a
```

有線LANで接続しているなら、`eth0`や`eth1`に、無線LANで接続しているなら`wlan0`や`wlan1`などに、`inet ~.~.~.~`といった感じでIPアドレスが表示されるはずです。一般家庭のLANなら下のような感じになるはず。
>wlan0:　flags=4163\<UP,BROADCAST,RUNNING,MULTICAST\>  mtu 1500
>&emsp;&emsp;&emsp;inet 192.168.1.22  netmask 255.255.255.0  broadcast 192.168.1.255

この場合、IPアドレスは192.168.1.22とわかります。

### 固定IPの設定

固定IPにしたほうが、後々楽になるので、いつもやってしまいます。

我が家の場合、ルーターのIPアドレスは`192.168.1.1`なので、ブラウザのアドレスバーにIPアドレスをぶち込んで、ブラウザから設定していきます。
DHCP固定IPアドレス設定を探し、MACアドレスとIPアドレスをひもづけて、設定を有効化します。
固定IPアドレスの設定をしても、しばらくは、反映されないので、アドレス解放をする必要があります。
コマンド引数は、必要に応じて変えてください。

```bash:新マシンで実行するコマンド
dhclient -r ネットワークインターフェイス
dhclient ネットワークインターフェイス

# 有線LANの場合
dhclient -r eth0 && dhclient eth0

# 無線LANの場合
dhclient -r wlan0 && dhclient wlan0
```

コマンドを実行した後、きちんと固定IPアドレスをもらえているか、`ifconfig`で確認します。

## キーのコピー

超絶便利なコマンドを実行します。

```bash:よく使うマシンで実行するコマンド
ssh-copy-id ユーザ名@IPアドレス

# 例
ssh-copy-id pi@192.168.1.101
```

もし、認証鍵がデフォルトのパスと違う場合、`-i`などで指定するとできるかもです。
`man`を見てください。

### 注意しておきたい-1

ポートが拒否される場合があるので、`/etc/ssh/sshd_config`の設定を変える。
`sudoedit`や`sudo vi`などのコマンドを実行して、
```config:/etc/ssh/sshd_config
...

Port 22

...
```
の行を有効にする。

### 注意しておきたい-2

次回システム起動時に、自動でsshのデーモンが動かない場合があるので、以下のコマンドを実行しておく。
```bash:sshを自動起動する
systemctl enable ssh
```

## エイリアスの設定

固定IPアドレスが取れたなら、エイリアスを設定した方が、毎回ユーザやIPアドレスを打つ必要がなくなります。

```config:よく使うマシンの~/.ssh/config
Host pi
    HostName 192.168.1.101
    User pi
```

こんな感じで書くことで、sshコマンドが楽になります。

```bash:よく使うマシンで実行するコマンド
# before
ssh pi@192.168.1.101
↓
# after
ssh pi
```

最高！

# 2.ユーザを追加する

ユーザを新しく作ります。

```bash:ユーザ作成
sudo adduser 名前
```

パスワードさえ入力すれば、あとはEnter連打で大丈夫です。

新ユーザにsudo権限を与えます。

```:sudoersに追加
sudo usermod -G sudo ユーザ名
```

`sudo cat /etc/group | grep sudo`
で確認して、ユーザが追加されていればOKです。

# 3.ファイアウォール

ufwをインストールします。

```bash:インストール
sudo apt update
sudo apt install ufw
```

ufwは Ubuntu FireWallの略だと思っていましたが、Uncomplicated FireWallの略らしいです。

```bash:ポート22を開けて有効化
sudo ufw allow 22
sudo ufw enable
```

ここで、22の許可を先にしないと、ssh接続していた場合、閉め出されることがあるので、注意してください。
また、`sudo ufw enable`を実行した時に、「コマンドにより、既存のssh接続が中断される場合があります。操作を続行しますか」と確認が出てきます。
しかし、ポート22でssh接続している場合、22を許可しているので問題ないです。
起動したら、念の為、確認しておきます。

```
sudo ufw status
```

# -1.最後


こんな感じのことをやっておくと良いと思います。
良い開発ライフを！
CircleCIのリモートDocker環境でBuildKitとレイヤーキャッシュを使えるようにする

CircleCIでDockerイメージをビルドするときに、何も設定していないとキャッシュが効かなかったり、マルチステージビルドを使ってると並列ビルドが効かなかったりして時間が無限に溶けていってしまっていました。

ここで、ちゃんと設定して使えるようにする方法を記したいと思います。


## レイヤーキャッシュを使えるようにする
とても簡単です。リモートDocker環境をセットアップするときに有効にしましょう。

```yaml
jobs:
  docker-image-build-job:
    steps:
      - setup_remote_docker:
          version: 20.10.11
          # ここでセットアップ時に有効にする
          docker_layer_caching: true
```

`docker_layer_caching: true` を指定してあげることで有効にできます。

参考: https://circleci.com/docs/ja/2.0/docker-layer-caching/#remote-docker-environment

以前はレイヤーキャッシュは有償プランでしか使用できませんでしたが、プランのページを見ると現在はFreeプランでも使用可能になっています。

参考: https://circleci.com/ja/pricing/

## BuildKitを使えるようにする
### 前提条件
大前提として、**BuildKitが使えるバージョンのDockerがリモートDocker環境とexecutorの双方に入っていること**が条件になります。

どちらかが欠けているとBuildKitは使えないので注意してください。

どのイメージにどのバージョンのDockerが含まれているかはCircleCIのイメージのページに記載されているので確認しましょう。

例として、[cimg/php:8.0.17](https://circleci.com/developer/ja/images/image/cimg/php)が内包しているパッケージを確認したところ以下のようになっていました。

> build-essential 12.8ubuntu1.1, composer 2.1.12, curl 7.68.0, docker 20.10.12, docker-compose-v1 version, docker-compose-v2 /usr/local/bin/docker-compose version, dockerize v0.6.1, git 2.34.1, jq 1.6, php 8.0.17, ubuntu 20.04.3 LTS, wget 1.20.3

この場合は `docker 20.10.12` となっていましたので使えますね。
(使えないバージョンを指定していてめっちゃハマったので要注意です)

### 設定する
設定自体はものすごく簡単です。

```yaml
jobs:
  docker-image-build-job:
    # 環境変数で有効化の指定をする
    environment:
      DOCKER_BUILDKIT: 1
    steps:
      - setup_remote_docker:
          version: 20.10.11
          docker_layer_caching: true
```

`DOCKER_BUILDKIT=1` という環境変数があるとBuildKitが有効になるので、`environment`で設定してしまいましょう。

外部のCircleCI Orbを使う場合にコマンドを書き換えられないときにも有効化できるので個人的には`environment`で有効にするのがおすすめです。
上位ディレクトリを維持したままファイルコピーする

## やりたいこと
こういうディレクトリがあったとして、
```
parent
├── child1
│   ├── grandchild1
│   └── grandchild2
└── child2
    ├── grandchild3
    └── grandchild4
```
ここだけ取り出したい。
```
 parent
    └── child1
        └── grandchild1
```

## やり方
### cpコマンド(Linuxのみ)
`--parent`オプションを使う。（ただしMacOSでは使えない）
```
cp -r --parents parent/child1/grandchild1 DESTINATION_DIRECTORY
```

### rsyncコマンド
`--relative`オプションを使う。
```
rsync -a --relative parent/child1/grandchild1 DESTINATION_DIRECTORY
```