├── .github └── FUNDING.yml ├── .gitignore ├── ARCHITECTURE.md ├── COPYING ├── Cargo.lock ├── Cargo.toml ├── README.md ├── chord_sim ├── chord_sim.py ├── modules │ ├── __init__.py │ ├── chord_node.py │ ├── chord_util.py │ ├── data_store.py │ ├── endpoints.py │ ├── gval.py │ ├── node_info.py │ ├── router.py │ ├── stabilizer.py │ └── taskqueue.py └── requirements.txt ├── chord_sim_rust ├── Cargo.lock ├── Cargo.toml └── src │ ├── chord_node.rs │ ├── chord_util.rs │ ├── data_store.rs │ ├── endpoints.rs │ ├── gval.rs │ ├── main.rs │ ├── node_info.rs │ ├── router.rs │ ├── stabilizer.rs │ └── taskqueue.rs ├── rust_env.txt ├── src ├── chord_node.rs ├── chord_util.rs ├── data_store.rs ├── endpoints.rs ├── gval.rs ├── main.rs ├── node_info.rs ├── router.rs └── stabilizer.rs └── tools ├── dkvs_client.go ├── rust_dkvs.bat └── rust_dkvs.sh /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [ryogrid] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | #Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | **/*.out 13 | 14 | -------------------------------------------------------------------------------- /ARCHITECTURE.md: -------------------------------------------------------------------------------- 1 | # Rustの手習い用に開発する分散KVS の設計メモ(注: 最終的にできたものとは異なる記述あり) 2 | 3 | - RedisやMemcachedのようなオンメモリでデータを保持するデータストアを実現する 4 | - データの他ノードによる中継などによってNAT越え(要は複数の異なるネットワークにノードが存在する場合)することまで考えると、実装がかなり複雑になることが予想されるため、今回は単一ネットワーク内に全ノードがいて、それらのノード間で直接通信が可能であることを前提とする 5 | - 各ノードは構成されたKVSへアクセスするための put, get, delete をREST API として提供する 6 | - 今回はパフォーマンスに重きを置かないこともあり、ストアできるデータはJSON形式として解釈可能なものに限定する(Valueの部分) 7 | - Keyの部分はそれほど大きくなることを想定せず、API呼び出し時のURLの部分に含める 8 | - KVSへのアクセスとは別に、DHTを構成するための通信も、(ひとまずは)REST APIとして定義し、ノード間のやり取りはそれを用いて行うものとする(いちいちソケットプログラミングするのは面倒なので・・)。コネクションを継続的に張り続けておかないと実装が複雑化するといったことが判明した場合は、見直しを行う 9 | - 分散ハッシュテーブル(Distributed Hash Table, DHT)の技術を用いて、中央サーバ無しで動作するものとする 10 | - DHTのアルゴリズム(プロトコル)としては一番ポピュラーな "Chord" を採用する 11 | - 参考にする資料・ウェブサイト 12 | - [分散システムにおけるScalableな名前付けアルゴリズム「Chord Protocol」を実装してみた - Qiita](https://qiita.com/taisho6339/items/7f849b65e2deab6759a1) 13 | - [ChordアルゴリズムによるDHT入門 - slideshare](https://www.slideshare.net/did2/chorddht) 14 | - [VIOPS04: DHTの基礎知識- slideshare](https://www.slideshare.net/viopsjp/dht20091211) 15 | - [Chord: A Scalable Peer- to-peer Lookup Protocol for Internet Applications - slideshare](https://www.slideshare.net/GertThijs/chord-presentation) 16 | - Chordネットワークの利用方法 17 | - Chordネットワーク 18 | - ChordネットワークはあくまでデータのIDから「担当ノード」のアドレスを得るための一種の名前解決の仕組みとして利用する 19 | - put, get, delete のいずれも、「担当ノード」とそのアドレスが判明してから、直接当該ノードに対して該当する操作を依頼する形とする 20 | - 広域分散のネットワークを構築する場合は、ルーティングや経路表の作成にあたって、NAT内に存在するノードへ対応するための考慮を加えなければならないと思われるが、それに加えて、UPnPを用いて、各ノードのデーモンがNATに穴を空けてグローバルIPでアクセス可能となるように振舞うことで、ネットワーク全体でグローバルIPでアクセス可能なノードの割合を増やさないと成立しないであろうと思われる 21 | - 実装にあたって 22 | - VIOSP04のスライドのP9に記述されている疑似コードであるが、ノード空間が6ビットより大きかったら再帰、もしくはループを用いた呼び出しがどこかに必要となると思われる 23 | - 一例としては、一番上の n.find_successor という関数を、n' を更新しながら回るwhileループにて、n'.find_predecessor が対象とするIDをsuccessorList内に含むpredecessor(n')まで到達して、そのn'から探している「担当ノード」の情報(とその後ろに位置する、successorListに含まれるノードの情報)を返してもらえるまで続ける 24 | - ネットワークのstabilizeが局所的に完了していない状態で、かつ、運悪く所望のデータを保持するノードが発見できなかった場合はエラーを返せば・・・よいか 25 | - データのIDを担当するはずのノード、もしくはその後続のノード群全てがデータを保持していない場合 26 | - 通信回りでのトラブルを考慮しない場合に、ネットワークの規模や、下層の実ネットワークの速度、各ノード処理時間を考慮した上で、探索に要する最悪時間が大体求まり、その時間以内にデータを得られないことが必ず判明するのかは現状不明(そうでない場合、つまり、探索処理におけるノード遷移が滅茶苦茶になり、いつになっても終了しないというケースが存在する場合、タイムアウトの処理も加える必要がある) 27 | - まずは、単純化のためにpredicessorもsuccessorも1ノードのみとする 28 | - データのレプリケーションは別途行うとして、新規にノードがjoinしてきたとしても、successorが ln(全ノード数) 程度あれば、データを新たな担当ノードに委譲せずとも、担当ノードが持っていない場合に順にsuccessorにgetをかけていけば良いのかもしれない 29 | - 委譲をするということであれば、定期的に自身の保持しているデータのうち担当外のものを、担当ノードを探索して、putするということをすれば良さそうだが、ある程度の個数があった場合、同一のノードに渡すものはまとめて渡すといったことが効率よくできるよう、データを保持しておくデータストアのデータ構造は考えておかなければならないだろう 30 | - 今回はひとまずノードの離脱が起きないという前提で実装を行うが、一通り動くものができたら、離脱が起きた場合のシーケンスについても考える必要がある。具体的には経路表に入っていたノードにアクセスできなかった場合にどうするか、を考えることが主となるはず。あとはデータのレプリケーション処理の追加が必要だろう 31 | - ストアしたデータはオンメモリだけで保持し、ストレージへの永続化はひとまず考えない 32 | - ストレージへの永続化はしないが、DHTのアドレス空間上での隣接ノード等を相手方としたデータのレプリケーションは実装する。これにより、全ノードないし 33 | 多数のノードを一斉に落とすといったことをしなければ、データは失われないようになる(はず) 34 | - 実装にはRust言語を用い、REST API実装のフレームワークや、極めて一般的な処理を担うライブラリを除き、フルスクラッチで実装する 35 | - Chordアルゴリズムの理解とそれに基づく設計の検証のため、簡易的な、Chordネットワークのシミュレータを Python(今のところの第一候補)を用いて作成する予定 36 | - 実システムを今回初めてまともに利用するRust言語で書いている中で、Chordの実装方法の誤りによる想定しない動作が発生した場合、デバッグのコストが非常に大きくなることが予想されるため 37 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 KANBAYASHI Ryo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | cargo-features = ["edition2021"] 2 | 3 | [package] 4 | name = "rust_dkvs" 5 | version = "0.1.0" 6 | authors = ["Ryo Kanbayashi "] 7 | edition = "2021" 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [dependencies] 12 | rocket = "0.4.10" 13 | rocket_contrib = { version = "0.4.5", features = ["json"] } 14 | serde = { version = "^1.0", features = ["derive"] } 15 | serde_json = "^1.0.68" 16 | hyper = "0.14.13" 17 | reqwest = { version = "0.11.5", features = ["blocking", "json"] } 18 | lazy_static = "1.4.0" 19 | # parking_lot = "0.11" 20 | clippy = { version = "*", optional = true } 21 | rand = "0.8.3" 22 | chrono = "0.4" 23 | #pprof = { version = "0.5.1", features = ["protobuf"] } 24 | 25 | [profile.release] 26 | panic = 'abort' 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FunnelKVS: Rust implementation of autonomous distributed key-value store which has REST interfaces 2 | 3 | - TODO list (Japanese, includes finished tasks) 4 | - https://gist.github.com/ryogrid/9b1f340babfbb6cdc9d03e57b7834e71 5 | 6 | ## What is Funnel? 7 | - It is name of wepons which appears in MOBILE SUIT GUNDUM (animation) series 8 | - Several type of funnel attacks enemy cooporate with multi units 9 | - https://www.google.com/search?q=fin+funnel&tbm=isch 10 | 11 | ## Distributed KVS 12 | - Architecture (Japanese) 13 | - https://github.com/ryogrid/rust_dkvs/blob/master/ARCHITECTURE.md 14 | - Referenced site (about REST API implementation. first one is Japanese) 15 | - https://qiita.com/yukinarit/items/c5128e67d168b4f39983 16 | - https://rocket.rs/v0.4/guide/getting-started/ 17 | 18 | - Fault torelance 19 | - **in-memory DB with no persistance (disk storage is not used)** 20 | - data replication is implemented 21 | - functionality for keeping network healthy at occuring node down is also implemented 22 | 23 | 24 | - Data consistency 25 | - care for data consistency is not enough yet... 26 | - First, **running test on occring node downs and joins condition is planned** 27 | 28 | - How to execute node daemon 29 | - $ curl https://sh.rustup.rs -sSf | /bin/bash -s -- -y --default-toolchain stable 30 | - $ rustup update && cargo update 31 | - **$ rustup install nightly-2021-07-29** 32 | - **$ rustup override set nightly-2021-07-29** 33 | - $ cargo build --release 34 | - $ target/release/rust_dkvs [born_id: 1...N] [IP addr to bind] [Port number to bind] [IP addr of medietor] [Port number of medietor] [log output path: currently not referenced] 35 | 36 | - Setup KVS system 37 | - **launch example of node daemons which compose KVS system** 38 | - **you can build KVS system which is composed of multiple machines if you place rust_dkvs program binary to the machines and kick these with some tool like SSH** 39 | - procedures wrote below are single machine example for easy trying 40 | - **born_id of first node must be 1** but born_id of other node has no restriction except thst **1** can't be used 41 | - **1** can be used by first node only 42 | - **IP address and port number has no restriction but all nodes should be able to communicate directly with each other** 43 | - $ target/release/rust_dkvs 1 127.0.0.1 11000 127.0.0.1 10999 ./ 44 | - $ target/release/rust_dkvs 2 127.0.0.1 11001 127.0.0.1 11000 ./ 45 | - $ target/release/rust_dkvs 3 127.0.0.1 11002 127.0.0.1 11000 ./ 46 | - .... 47 | 48 | - REST interfaces which are offered by KVS system 49 | - for easy testing with Web browser or something (use http GET request) 50 | - http://[node addr]:[node_port]/global_put_simple?key=[string only includes ascii character]&val=[string only includes ascii character] 51 | - http://[node addr]:[node_port]/global_get_simple?key=[string only includes ascii character] 52 | - http://[node addr]:[node_port]/global_delete_simple?key=[string only includes ascii character] 53 | - for using from code or HTTP client tool (use http POST request to send JSON text) 54 | - **Data should be appropriately escaped as JSON string and charactor code should be UTF-8** 55 | - **"Content-Type" header's value should be "application/json"** 56 | - http://[node addr]:[node_port]/global_put 57 |   - body at POST -> { "key_str" : "[charactors]", "val_str" : "[charactors]" } 58 | - http://[node addr]:[node_port]/global_get 59 |   - body at POST -> "[key charactors]" 60 | - http://[node addr]:[node_port]/global_delete 61 | - body at POST -> "[key charactors]" 62 | 63 | - Utility CLI tool (tools/dkvs_client.go) 64 | - setup chord network (on local machine) 65 | - $ go run -op=setup-nodes -arg1=[launch nodes num] 66 | - KVS system network health check 67 | - $ go run -op=check-chain 68 | - if the network is helthy, launched or alive nodes (process) are listed without duplication 69 | - testing put values to KVS via specified node 70 | - $ go run -op=put-test-values -arg1="127.0.0.1:11000" 71 | - put 100 values 72 | - node you specify with arg1 has no restiction like that local addresses can be only specified. above is example 73 | - testing get already put values from KVS via specified node 74 | - $ go run -op=get-test-values -arg1="127.0.0.1:11005" 75 | - get 100 values 76 | 77 | ## Simulator of distributed KVS (chord_sim dir) 78 | - design verification with simulator wrote by **Python** (**verification is finished**) 79 | - you can simulate distributed kvs working behavior. on the simulation put, get, stabilize, join operations are issued continuously on node downs and node joins occuring condition. 80 | 81 | ## Simulator of distributed KVS (chord_sim_rust dir) 82 | - design verification with simulator wrote by **Rust** (**verification is finished**) 83 | - Rust implemantation of chord simulator can be executed like below 84 | - $ curl https://sh.rustup.rs -sSf | /bin/bash -s -- -y --default-toolchain nightly 85 | - $ rustup override set nightly 86 | - $ rustup update && cargo update 87 | - $ cd chord_sim_rust 88 | - $ cargo run 89 | 90 | ## Runnable platforms for KVS system (= you can build the daemon binary for the platform) 91 | - Windows native 92 | - dev env is also OK 93 | - macOS 94 | - dev env is also OK 95 | - Linux (and Windows Subsystem for Linux environment) 96 | - dev env is also OK 97 | - probably ... 98 | - other UNIX like OS environments 99 | - please try! 100 | -------------------------------------------------------------------------------- /chord_sim/modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryogrid/FunnelKVS/8daa6a1b399089544f7cb683924b359c773d00a4/chord_sim/modules/__init__.py -------------------------------------------------------------------------------- /chord_sim/modules/chord_node.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | from typing import Dict, List, Tuple, Optional, cast 4 | 5 | import sys 6 | import modules.gval as gval 7 | from .node_info import NodeInfo 8 | from .data_store import DataStore 9 | from .stabilizer import Stabilizer 10 | from .router import Router 11 | from .taskqueue import TaskQueue 12 | from .endpoints import Endpoints 13 | from .chord_util import ChordUtil, NodeIsDownedExceptiopn, AppropriateNodeNotFoundException, \ 14 | InternalControlFlowException, DataIdAndValue, ErrorCode 15 | 16 | class ChordNode: 17 | QUERIED_DATA_NOT_FOUND_STR = "QUERIED_DATA_WAS_NOT_FOUND" 18 | OP_FAIL_DUE_TO_FIND_NODE_FAIL_STR = "OPERATION_FAILED_DUE_TO_FINDING_NODE_FAIL" 19 | 20 | # global_get内で探索した担当ノードにgetをかけて、データを持っていないと 21 | # レスポンスがあった際に、持っていないか辿っていくノードの一方向における上限数 22 | GLOBAL_GET_NEAR_NODES_TRY_MAX_NODES = 5 23 | 24 | # global_getでの取得が NOT_FOUNDになった場合はこのクラス変数に格納して次のget処理の際にリトライさせる 25 | # なお、本シミュレータの設計上、このフィールドは一つのデータだけ保持できれば良い 26 | need_getting_retry_data_id : int = -1 27 | need_getting_retry_node : Optional['ChordNode'] = None 28 | 29 | # global_put が router.find_successorでの例外発生で失敗した場合にこのクラス変数に格納して次のput処理の際にリトライさせる 30 | # なお、本シミュレータの設計上、このフィールドは一つのデータだけ保持できれば良い 31 | need_put_retry_data_id : int = -1 32 | need_put_retry_data_value : str = "" 33 | need_put_retry_node : Optional['ChordNode'] = None 34 | 35 | # join処理もコンストラクタで行ってしまう 36 | def __init__(self, node_address: str, first_node=False): 37 | self.node_info : NodeInfo = NodeInfo() 38 | 39 | self.data_store : DataStore = DataStore(self) 40 | self.stabilizer : Stabilizer = Stabilizer(self) 41 | self.router : Router = Router(self) 42 | self.tqueue : TaskQueue = TaskQueue(self) 43 | self.endpoints : Endpoints = Endpoints(self) 44 | 45 | # ミリ秒精度のUNIXTIMEから自身のアドレスにあたる文字列と、Chordネットワーク上でのIDを決定する 46 | self.node_info.address_str = ChordUtil.gen_address_str() 47 | self.node_info.node_id = ChordUtil.hash_str_to_int(self.node_info.address_str) 48 | 49 | gval.already_born_node_num += 1 50 | self.node_info.born_id = gval.already_born_node_num 51 | 52 | # シミュレーション時のみ必要なフィールド(実システムでは不要) 53 | self.is_alive = True 54 | 55 | # join処理が完了していない状態で global_get, global_put, stablize処理, kill処理 がシミュレータの 56 | # 大本から呼び出されないようにするためのフラグ 57 | self.is_join_op_finished = False 58 | 59 | if first_node: 60 | with self.node_info.lock_of_pred_info, self.node_info.lock_of_succ_infos: 61 | # 最初の1ノードの場合 62 | 63 | # successorとpredecessorは自身として終了する 64 | self.node_info.successor_info_list.append(self.node_info.get_partial_deepcopy()) 65 | self.node_info.predecessor_info = self.node_info.get_partial_deepcopy() 66 | 67 | # 最初の1ノードなので、joinメソッド内で行われるsuccessor からの 68 | # データの委譲は必要ない 69 | 70 | return 71 | else: 72 | self.stabilizer.join(node_address) 73 | 74 | def global_put(self, data_id : int, value_str : str) -> bool: 75 | # try: 76 | 77 | #target_node = self.router.find_successor(data_id) 78 | 79 | ret = self.router.find_successor(data_id) 80 | if (ret.is_ok): 81 | target_node: 'ChordNode' = cast('ChordNode', ret.result) 82 | # リトライは不要であったため、リトライ用情報の存在を判定するフィールドを 83 | # 初期化しておく 84 | ChordNode.need_put_retry_data_id = -1 85 | else: # ret.err_code == ErrorCode.AppropriateNodeNotFoundException_CODE || ret.err_code == ErrorCode.InternalControlFlowException_CODE || ret.err_code == ErrorCode.NodeIsDownedException_CODE 86 | # 適切なノードを得られなかった、もしくは join処理中のノードを扱おうとしてしまい例外発生 87 | # となってしまったため次回呼び出し時にリトライする形で呼び出しをうけられるように情報を設定しておく 88 | ChordNode.need_put_retry_data_id = data_id 89 | ChordNode.need_put_retry_node = self 90 | ChordUtil.dprint("global_put_1,RETRY_IS_NEEDED" + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 91 | + ChordUtil.gen_debug_str_of_data(data_id)) 92 | return False 93 | 94 | # except (AppropriateNodeNotFoundException, InternalControlFlowException, NodeIsDownedExceptiopn): 95 | # # 適切なノードを得られなかった、もしくは join処理中のノードを扱おうとしてしまい例外発生 96 | # # となってしまったため次回呼び出し時にリトライする形で呼び出しをうけられるように情報を設定しておく 97 | # ChordNode.need_put_retry_data_id = data_id 98 | # ChordNode.need_put_retry_node = self 99 | # ChordUtil.dprint("global_put_1,RETRY_IS_NEEDED" + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 100 | # + ChordUtil.gen_debug_str_of_data(data_id)) 101 | # return False 102 | 103 | success = target_node.endpoints.grpc__put(data_id, value_str) 104 | if not success: 105 | ChordNode.need_put_retry_data_id = data_id 106 | ChordNode.need_put_retry_node = self 107 | ChordUtil.dprint("global_put_2,RETRY_IS_NEEDED" + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 108 | + ChordUtil.gen_debug_str_of_data(data_id)) 109 | return False 110 | 111 | # TODO: x direct access to node_info of target_node at global_put 112 | ChordUtil.dprint("global_put_3," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 113 | + ChordUtil.gen_debug_str_of_node(target_node.node_info) + "," 114 | + ChordUtil.gen_debug_str_of_data(data_id)) 115 | 116 | return True 117 | 118 | def put(self, data_id : int, value_str : str) -> bool: 119 | ChordUtil.dprint("put_0," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 120 | + ChordUtil.gen_debug_str_of_data(data_id)) 121 | 122 | if self.is_alive == False: 123 | # 処理の合間でkillされてしまっていた場合の考慮 124 | # 何もしないで終了する 125 | ChordUtil.dprint("put_0_5," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 126 | + "REQUEST_RECEIVED_BUT_I_AM_ALREADY_DEAD") 127 | return False 128 | 129 | # 担当範囲(predecessorのidと自身のidの間)のデータであるかのチェック処理を加える 130 | # そこに収まっていなかった場合、一定時間後リトライが行われるようエラーを返す. 131 | # リクエストを受けるという実装も可能だが、stabilize処理で predecessor が生きて 132 | # いるノードとなるまで下手にデータを持たない方が、データ配置の整合性を壊すリスクが 133 | # 減りそうな気がするので、そうする 134 | if self.node_info.predecessor_info == None: 135 | return False 136 | # Chordネットワークを右回りにたどった時に、データの id (data_id) が predecessor の node_id から 137 | # 自身の node_id の間に位置する場合、そのデータは自身の担当だが、そうではない場合 138 | if not ChordUtil.exist_between_two_nodes_right_mawari(cast(NodeInfo,self.node_info.predecessor_info).node_id, self.node_info.node_id, data_id): 139 | return False 140 | 141 | if self.node_info.lock_of_succ_infos.acquire(timeout=gval.LOCK_ACQUIRE_TIMEOUT) == False: 142 | # 今回は失敗としてしまう 143 | ChordUtil.dprint("put_1," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 144 | + "LOCK_ACQUIRE_TIMEOUT") 145 | return False 146 | try: 147 | with self.node_info.lock_of_datastore: 148 | self.data_store.store_new_data(data_id, value_str) 149 | self.data_store.distribute_replica() 150 | finally: 151 | self.node_info.lock_of_succ_infos.release() 152 | 153 | ChordUtil.dprint("put_4," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 154 | + ChordUtil.gen_debug_str_of_data(data_id) + "," + value_str) 155 | 156 | return True 157 | 158 | # global_getで取得しようとしたKeyが探索したノードに存在なかった場合に、当該ノードから 159 | # predecessorを辿ってリカバリを試みる処理をくくり出したもの 160 | def global_get_recover_prev(self, data_id : int) -> Tuple[str, Optional['ChordNode']]: 161 | if self.node_info.lock_of_pred_info.acquire(timeout=gval.LOCK_ACQUIRE_TIMEOUT) == False: 162 | ChordUtil.dprint("global_get_recover_prev_0," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 163 | + "LOCK_ACQUIRE_TIMEOUT") 164 | return ChordNode.QUERIED_DATA_NOT_FOUND_STR, None 165 | try: 166 | if self.node_info.predecessor_info == None: 167 | ChordUtil.dprint("global_get_recover_prev_1,predecessor is None") 168 | return ChordNode.QUERIED_DATA_NOT_FOUND_STR, None 169 | # try: 170 | 171 | # cur_predecessor : ChordNode = ChordUtil.get_node_by_address( 172 | # cast(NodeInfo, self.node_info.predecessor_info).address_str) 173 | ret = ChordUtil.get_node_by_address(cast(NodeInfo, self.node_info.predecessor_info).address_str) 174 | if (ret.is_ok): 175 | cur_predecessor : 'ChordNode' = cast('ChordNode', ret.result) 176 | got_value_str = cur_predecessor.endpoints.grpc__get(data_id, for_recovery=True) 177 | else: # ret.is_ok == False 178 | if cast(int,ret.err_code) == ErrorCode.NodeIsDownedException_CODE: 179 | # ここでは何も対処はしない 180 | ChordUtil.dprint("global_get_recover_prev_2,NODE_IS_DOWNED") 181 | return ChordNode.QUERIED_DATA_NOT_FOUND_STR, None 182 | else: #cast(int,ret.err_code) == ErrorCode.InternalControlFlowException_CODE 183 | # join処理中のノードにアクセスしようとしてしまった場合に内部的にraiseされる例外 184 | ChordUtil.dprint("global_get_recover_prev_3,TARGET_NODE_DOES_NOT_EXIST_EXCEPTION_IS_OCCURED") 185 | return ChordNode.QUERIED_DATA_NOT_FOUND_STR, None 186 | 187 | # except NodeIsDownedExceptiopn: 188 | # # ここでは何も対処はしない 189 | # ChordUtil.dprint("global_get_recover_prev_2,NODE_IS_DOWNED") 190 | # return ChordNode.QUERIED_DATA_NOT_FOUND_STR, None 191 | # except InternalControlFlowException: 192 | # # join処理中のノードにアクセスしようとしてしまった場合に内部的にraiseされる例外 193 | # ChordUtil.dprint("global_get_recover_prev_3,TARGET_NODE_DOES_NOT_EXIST_EXCEPTION_IS_OCCURED") 194 | # return ChordNode.QUERIED_DATA_NOT_FOUND_STR, None 195 | 196 | ChordUtil.dprint("global_get_recover_prev_4," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 197 | + ChordUtil.gen_debug_str_of_data(data_id)) 198 | if got_value_str != ChordNode.QUERIED_DATA_NOT_FOUND_STR: 199 | # データが円環上でIDが小さくなっていく方向(反時計時計回りの方向)を前方とした場合に 200 | # 前方に位置するpredecessorを辿ることでデータを取得することができた 201 | # TODO: x direct access to node_info of cur_predecessor at global_get 202 | ChordUtil.dprint("global_get_recover_prev_5," 203 | + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 204 | + "data found at predecessor," 205 | + ChordUtil.gen_debug_str_of_node(cur_predecessor.node_info)) 206 | return got_value_str, cur_predecessor 207 | else: 208 | # できなかった 209 | # TODO: x direct access to node_info of cur_predecessor at global_get 210 | ChordUtil.dprint("global_get_recover_prev_6," 211 | + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 212 | + "data not found at predecessor," 213 | + ChordUtil.gen_debug_str_of_node(cur_predecessor.node_info)) 214 | return ChordNode.QUERIED_DATA_NOT_FOUND_STR, cur_predecessor 215 | finally: 216 | self.node_info.lock_of_pred_info.release() 217 | 218 | # 他の例外の発生ででここに到達した 219 | return ChordNode.QUERIED_DATA_NOT_FOUND_STR, None 220 | 221 | # global_getで取得しようとしたKeyが探索したノードに存在なかった場合に、当該ノードから 222 | # successorを辿ってリカバリを試みる処理をくくり出したもの 223 | def global_get_recover_succ(self, data_id : int) -> Tuple[str, Optional['ChordNode']]: 224 | # try: 225 | # cur_successor : ChordNode = ChordUtil.get_node_by_address( 226 | # cast(NodeInfo, self.node_info.successor_info_list[0]).address_str) 227 | # got_value_str = cur_successor.endpoints.grpc__get(data_id, for_recovery=True) 228 | 229 | ret = ChordUtil.get_node_by_address(cast(NodeInfo, self.node_info.successor_info_list[0]).address_str) 230 | if (ret.is_ok): 231 | cur_successor : 'ChordNode' = cast('ChordNode', ret.result) 232 | got_value_str = cur_successor.endpoints.grpc__get(data_id, for_recovery=True) 233 | else: # ret.is_ok == False 234 | if cast(int,ret.err_code) == ErrorCode.NodeIsDownedException_CODE: 235 | # ここでは何も対処はしない 236 | ChordUtil.dprint("global_get_recover_succ_2,NODE_IS_DOWNED") 237 | return ChordNode.QUERIED_DATA_NOT_FOUND_STR, None 238 | else: #cast(int,ret.err_code) == ErrorCode.InternalControlFlowException_CODE 239 | # join処理中のノードにアクセスしようとしてしまった場合に内部的にraiseされる例外 240 | ChordUtil.dprint("global_get_recover_succ_3,TARGET_NODE_DOES_NOT_EXIST_EXCEPTION_IS_OCCURED") 241 | return ChordNode.QUERIED_DATA_NOT_FOUND_STR, None 242 | 243 | # except NodeIsDownedExceptiopn: 244 | # # ここでは何も対処はしない 245 | # ChordUtil.dprint("global_get_recover_succ_2,NODE_IS_DOWNED") 246 | # return ChordNode.QUERIED_DATA_NOT_FOUND_STR, None 247 | # except InternalControlFlowException: 248 | # # join処理中のノードにアクセスしようとしてしまった場合に内部的にraiseされる例外 249 | # ChordUtil.dprint("global_get_recover_succ_3,TARGET_NODE_DOES_NOT_EXIST_EXCEPTION_IS_OCCURED") 250 | # return ChordNode.QUERIED_DATA_NOT_FOUND_STR, None 251 | 252 | ChordUtil.dprint("global_get_recover_succ_4," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 253 | + ChordUtil.gen_debug_str_of_data(data_id)) 254 | 255 | if got_value_str != ChordNode.QUERIED_DATA_NOT_FOUND_STR: 256 | # データが円環上でIDが小さくなっていく方向(反時計時計回りの方向)を前方とした場合に 257 | # 前方に位置するsuccessorを辿ることでデータを取得することができた 258 | # TODO: x direct access to node_info of cur_successor at global_get 259 | ChordUtil.dprint("global_get_recover_succ_5," 260 | + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 261 | + "data found at successor," 262 | + ChordUtil.gen_debug_str_of_node(cur_successor.node_info)) 263 | return got_value_str, cur_successor 264 | else: 265 | # できなかった 266 | # TODO: x direct access to node_info of cur_successor at global_get 267 | ChordUtil.dprint("global_get_recover_succ_6," 268 | + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 269 | + "data not found at successor," 270 | + ChordUtil.gen_debug_str_of_node(cur_successor.node_info)) 271 | return ChordNode.QUERIED_DATA_NOT_FOUND_STR, cur_successor 272 | 273 | # 他の例外の発生ででここに到達した 274 | return ChordNode.QUERIED_DATA_NOT_FOUND_STR, None 275 | 276 | # 得られた value の文字列を返す 277 | # データの取得に失敗した場合は ChordNode.QUERIED_DATA_NOT_FOUND_STR を返す 278 | # 取得対象のデータが削除済みのデータであった場合は DataStore.DELETED_ENTRY_MARKING_STR を返す 279 | # TODO: 現状の実装では、データの取得に失敗した場合、そのエントリが過去にputされていないためなのか、システム側の都合による 280 | # ものなのかは区別がつかない. 281 | # 実システムでは一定回数リトライを行い、それでもダメな場合は ChordNode.QUERIED_DATA_NOT_FOUND_STR を返すという 282 | # 形にしなければならない at global_get 283 | def global_get(self, data_id : int) -> str: 284 | ChordUtil.dprint("global_get_0," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 285 | + ChordUtil.gen_debug_str_of_data(data_id)) 286 | 287 | # try: 288 | # target_node = self.router.find_successor(data_id) 289 | # got_value_str = target_node.endpoints.grpc__get(data_id) 290 | 291 | ret = self.router.find_successor(data_id) 292 | if (ret.is_ok): 293 | target_node: 'ChordNode' = cast('ChordNode', ret.result) 294 | got_value_str = target_node.endpoints.grpc__get(data_id) 295 | else: 296 | # ret.err_code == ErrorCode.AppropriateNodeNotFoundException_CODE || ret.err_code == ErrorCode.InternalControlFlowException_CODE 297 | # || ret.err_code == ErrorCode.NodeIsDownedException_CODE 298 | 299 | # 適切なノードを得ることができなかった、もしくは、内部エラーが発生した 300 | 301 | # リトライに必要な情報をクラス変数に設定しておく 302 | ChordNode.need_getting_retry_data_id = data_id 303 | ChordNode.need_getting_retry_node = self 304 | 305 | ChordUtil.dprint("global_get_0_1,FIND_NODE_FAILED," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 306 | + ChordUtil.gen_debug_str_of_data(data_id)) 307 | # 処理を終える 308 | return ChordNode.OP_FAIL_DUE_TO_FIND_NODE_FAIL_STR 309 | 310 | # except (AppropriateNodeNotFoundException, InternalControlFlowException, NodeIsDownedExceptiopn): 311 | # # 適切なノードを得ることができなかった、もしくは、内部エラーが発生した 312 | # 313 | # # リトライに必要な情報をクラス変数に設定しておく 314 | # ChordNode.need_getting_retry_data_id = data_id 315 | # ChordNode.need_getting_retry_node = self 316 | # 317 | # ChordUtil.dprint("global_get_0_1,FIND_NODE_FAILED," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 318 | # + ChordUtil.gen_debug_str_of_data(data_id)) 319 | # # 処理を終える 320 | # return ChordNode.OP_FAIL_DUE_TO_FIND_NODE_FAIL_STR 321 | 322 | is_data_got_on_recovery = False 323 | # 返ってきた値が ChordNode.QUERIED_DATA_NOT_FOUND_STR だった場合、target_nodeから 324 | # 一定数の predecessorを辿ってそれぞれにも data_id に対応するデータを持っていないか問い合わせるようにする 325 | if got_value_str == ChordNode.QUERIED_DATA_NOT_FOUND_STR: 326 | tried_node_num = 0 327 | # 最初は処理の都合上、最初にgetをかけたノードを設定する 328 | cur_predecessor : 'ChordNode' = target_node 329 | while tried_node_num < ChordNode.GLOBAL_GET_NEAR_NODES_TRY_MAX_NODES: 330 | ChordUtil.dprint("global_get_1," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 331 | + ChordUtil.gen_debug_str_of_data(data_id) + "," 332 | + got_value_str + "," + str(tried_node_num)) 333 | 334 | got_value_str, tmp_cur_predecessor = cur_predecessor.endpoints.grpc__global_get_recover_prev(data_id) 335 | if got_value_str != ChordNode.QUERIED_DATA_NOT_FOUND_STR: 336 | is_data_got_on_recovery = True 337 | break 338 | else: 339 | tried_node_num += 1 340 | 341 | if tmp_cur_predecessor != None: 342 | cur_predecessor = cast('ChordNode', tmp_cur_predecessor) 343 | 344 | # 返ってきた値が ChordNode.QUERIED_DATA_NOT_FOUND_STR だった場合、target_nodeから 345 | # 一定数の successor を辿ってそれぞれにも data_id に対応するデータを持っていないか問い合わせるようにする 346 | if got_value_str == ChordNode.QUERIED_DATA_NOT_FOUND_STR: 347 | tried_node_num = 0 348 | # 最初は処理の都合上、最初にgetをかけたノードを設定する 349 | cur_successor = target_node 350 | while tried_node_num < ChordNode.GLOBAL_GET_NEAR_NODES_TRY_MAX_NODES: 351 | ChordUtil.dprint("global_get_2," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 352 | + ChordUtil.gen_debug_str_of_data(data_id) + "," 353 | + got_value_str + "," + str(tried_node_num)) 354 | 355 | got_value_str, tmp_cur_successor = cur_successor.endpoints.grpc__global_get_recover_succ(data_id) 356 | if got_value_str != ChordNode.QUERIED_DATA_NOT_FOUND_STR: 357 | is_data_got_on_recovery = True 358 | break 359 | else: 360 | tried_node_num += 1 361 | 362 | if tmp_cur_successor != None: 363 | cur_successor = cast('ChordNode', tmp_cur_successor) 364 | 365 | # リトライを試みたであろう時の処理 366 | if ChordNode.need_getting_retry_data_id != -1: 367 | if got_value_str != ChordNode.QUERIED_DATA_NOT_FOUND_STR: 368 | # リトライに成功した 369 | ChordUtil.dprint("global_get_2_6,retry of global_get is succeeded") 370 | # リトライは不要なためクリア 371 | ChordNode.need_getting_retry_data_id = -1 372 | ChordNode.need_getting_retry_node = None 373 | else: 374 | # リトライに失敗した(何もしない) 375 | ChordUtil.dprint("global_get_2_7,retry of global_get is failed") 376 | pass 377 | 378 | # 取得に失敗した場合はリトライに必要な情報をクラス変数に設定しておく 379 | if got_value_str == ChordNode.QUERIED_DATA_NOT_FOUND_STR: 380 | ChordNode.need_getting_retry_data_id = data_id 381 | ChordNode.need_getting_retry_node = self 382 | 383 | if is_data_got_on_recovery == True: 384 | # リカバリ処理でデータを取得した場合は自身のデータストアにもその値を保持しておく 385 | self.data_store.store_new_data(data_id, got_value_str) 386 | 387 | # TODO: x direct access to node_info of target_node at global_get 388 | ChordUtil.dprint("global_get_3," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 389 | + ChordUtil.gen_debug_str_of_node(target_node.node_info) + "," 390 | + ChordUtil.gen_debug_str_of_data(data_id) + "," + got_value_str) 391 | return got_value_str 392 | 393 | # 得られた value の文字列を返す 394 | def get(self, data_id : int, for_recovery = False) -> str: 395 | if self.is_alive == False: 396 | # 処理の合間でkillされてしまっていた場合の考慮 397 | # 何もしないで終了する 398 | ChordUtil.dprint("get_0," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 399 | + "REQUEST_RECEIVED_BUT_I_AM_ALREADY_DEAD") 400 | return ChordNode.OP_FAIL_DUE_TO_FIND_NODE_FAIL_STR 401 | 402 | if self.node_info.predecessor_info == None: 403 | # まだpredecessorが設定されれていなかった場合の考慮 404 | ChordUtil.dprint("get_0_5," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 405 | + "REQUEST_RECEIVED_BUT_I_CAN_NOT_KNOW_TANTOU_RANGE") 406 | return ChordNode.QUERIED_DATA_NOT_FOUND_STR 407 | 408 | # try: 409 | #di_entry : DataIdAndValue = self.data_store.get(data_id) 410 | ret = self.data_store.get(data_id) 411 | if (ret.is_ok): 412 | di_entry: DataIdAndValue = cast(DataIdAndValue, ret.result) 413 | else: # ret.err_code == ErrorCode.KeyError_CODE 414 | err_str = ChordNode.QUERIED_DATA_NOT_FOUND_STR 415 | ChordUtil.dprint("get_1," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 416 | + ChordUtil.gen_debug_str_of_data(data_id) + "," + err_str) 417 | return err_str 418 | # except KeyError: 419 | # err_str = ChordNode.QUERIED_DATA_NOT_FOUND_STR 420 | # ChordUtil.dprint("get_1," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 421 | # + ChordUtil.gen_debug_str_of_data(data_id) + "," + err_str) 422 | # return err_str 423 | 424 | # Chordネットワークを右回りにたどった時に、データの id (data_id) がpredecessorの node_id から 425 | # 自身の node_id の間に位置した. 426 | # つまり、自身の担当ID範囲であった 427 | if ChordUtil.exist_between_two_nodes_right_mawari(cast('NodeInfo', self.node_info.predecessor_info).node_id, 428 | self.node_info.node_id, 429 | data_id) or for_recovery == True: 430 | # 担当ノード(マスター)のデータであったか、担当ノードとしてgetを受け付けたがデータを持っていなかったために 431 | # 周囲のノードに当該データを持っていないか問い合わせる処理を行っていた場合 432 | ret_value_str = di_entry.value_data 433 | ChordUtil.dprint("get_2," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 434 | + ChordUtil.gen_debug_str_of_data(data_id) + "," + ret_value_str) 435 | else: 436 | # 自身の担当範囲のIDのデータでは無かった 437 | # 該当IDのデータを保持していたとしてもレプリカであるので返さずにエラー文字列を返す 438 | ret_value_str = self.QUERIED_DATA_NOT_FOUND_STR 439 | 440 | ChordUtil.dprint("get_3," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 441 | + ChordUtil.gen_debug_str_of_data(data_id) + "," + ret_value_str) 442 | 443 | ChordUtil.dprint("get_4," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 444 | + ChordUtil.gen_debug_str_of_data(data_id) + "," + ret_value_str) 445 | 446 | return ret_value_str 447 | 448 | # 指定されたデータが存在した場合は true を返し、そうでない場合は false を返す 449 | # TODO: global_getとglobal_putを呼び出しているがそれぞれで発見したノードが異なった場合 450 | # を考慮すると、もう少し手のこんだ実装を行わなければならないかもしれない. 451 | # また、global_getを何度かリトライすることでデータが見つかる場合が存在することを考える 452 | # と、global_getもしくはglobal_putをある程度の回数呼び出す必要があるかもしれないが、 453 | # 当然、このRPCのレスポンスタイムは大きく落ちるためどうすべきか悩ましい. 454 | def global_delete(self, data_id : int) -> bool: 455 | cur_val = self.global_get(data_id) 456 | self.global_put(data_id, DataStore.DELETED_ENTRY_MARKING_STR) 457 | return not (cur_val == ChordNode.QUERIED_DATA_NOT_FOUND_STR 458 | or cur_val == DataStore.DELETED_ENTRY_MARKING_STR) 459 | 460 | def pass_node_info(self) -> 'NodeInfo': 461 | return self.node_info.get_partial_deepcopy() 462 | 463 | # TODO: 実システムでのみ利用される. 他ノードのChordNodeオブジェクトはデフォルトで 464 | # successor_info_listが空リストとなっているので、その内容をrpc呼び出しを 465 | # 行って取得したデータで埋める 466 | def fill_succ_info_list(self): 467 | if self.node_info.lock_of_succ_infos.acquire(timeout=gval.LOCK_ACQUIRE_TIMEOUT) == False: 468 | ChordUtil.dprint("fill_succ_info_list_1," + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 469 | + "LOCK_ACQUIRE_TIMEOUT") 470 | try: 471 | self.node_info.successor_info_list = self.endpoints.grpc__pass_successor_list() 472 | finally: 473 | self.node_info.lock_of_succ_infos.release() 474 | -------------------------------------------------------------------------------- /chord_sim/modules/chord_util.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | import sys 4 | import time 5 | import random 6 | import datetime 7 | import dataclasses 8 | import traceback 9 | from typing import List, Any, Optional, TypeVar, Generic, Union, cast, TYPE_CHECKING 10 | 11 | from . import gval 12 | 13 | if TYPE_CHECKING: 14 | from .chord_node import ChordNode 15 | from .node_info import NodeInfo 16 | 17 | class ErrorCode: 18 | KeyError_CODE = 1 19 | NodeIsDownedException_CODE = 2 20 | AppropriateNodeNotFoundException_CODE = 3 21 | InternalControlFlowException_CODE = 4 22 | 23 | T = TypeVar('T') 24 | 25 | class PResult(Generic[T]): 26 | 27 | @classmethod 28 | def Ok(cls, result: T) -> 'PResult[T]': 29 | return PResult[T](result, True) 30 | 31 | @classmethod 32 | def Err(cls, result: T, err_code : int) -> 'PResult[T]': 33 | return PResult[T](result, False, err_code = err_code) 34 | 35 | def __init__(self, result: T, is_ok: bool, err_code = None): 36 | self.result : T = result 37 | self.err_code : Optional[int] = err_code 38 | self.is_ok : bool = is_ok 39 | 40 | class ChordUtil: 41 | # 任意の文字列をハッシュ値(定められたbit数で表現される整数値)に変換しint型で返す 42 | # アルゴリズムはSHA1, 160bitで表現される正の整数となる 43 | # メモ: 10進数の整数は組み込みの hex関数で 16進数表現での文字列に変換可能 44 | # TODO: 本来のハッシュ関数に戻す必要あり hash_str_to_int 45 | @classmethod 46 | def hash_str_to_int(cls, input_str : str) -> int: 47 | # hash_hex_str = hashlib.sha1(input_str.encode()).hexdigest() 48 | # hash_id_num = int(hash_hex_str, 16) 49 | 50 | # TODO: ID_SPACE_BITS ビットで表現できる符号なし整数をID空間とする. 51 | # 通常、ID_SPACE_BITS は sha1 で 160 となるが、この検証コードでは 52 | # ハッシュ関数を用いなくても問題の起きない実装となっているため、より小さい 53 | # ビット数で表現可能な IDスペース 内に収まる値を乱数で求めて返す 54 | hash_id_num = random.randint(0, gval.ID_SPACE_RANGE - 1) 55 | return hash_id_num 56 | 57 | # 与えたリストの要素のうち、ランダムに選択した1要素を返す 58 | @classmethod 59 | def get_random_elem(cls, list_like : List[Any]) -> Any: 60 | length = len(list_like) 61 | idx = random.randint(0, length - 1) 62 | return list_like[idx] 63 | 64 | @classmethod 65 | def get_random_data(cls) -> 'KeyValue': 66 | # with gval.lock_of_all_data_list: 67 | return ChordUtil.get_random_elem(gval.all_data_list) 68 | 69 | # UNIXTIME(ミリ秒精度)にいくつか値を加算した値からアドレス文字列を生成する 70 | @classmethod 71 | def gen_address_str(cls) -> str: 72 | return str(time.time() + 10) 73 | 74 | # 計算したID値がID空間の最大値を超えていた場合は、空間内に収まる値に変換する 75 | @classmethod 76 | def overflow_check_and_conv(cls, id : int) -> int: 77 | ret_id = id 78 | if id > gval.ID_MAX: 79 | # 1を足すのは MAX より 1大きい値が 0 となるようにするため 80 | ret_id = id - (gval.ID_MAX + 1) 81 | return ret_id 82 | 83 | # idがID空間の最大値に対して何パーセントの位置かを適当な精度の浮動小数の文字列 84 | # にして返す 85 | @classmethod 86 | def conv_id_to_ratio_str(cls, id : int) -> str: 87 | ratio = (id / gval.ID_MAX) * 100.0 88 | return '%2.4f' % ratio 89 | 90 | # ID空間が環状になっていることを踏まえて base_id から前方をたどった場合の 91 | # ノード間の距離を求める 92 | # ここで前方とは、IDの値が小さくなる方向である 93 | @classmethod 94 | def calc_distance_between_nodes_left_mawari(cls, base_id : int, target_id : int) -> int: 95 | # successorが自分自身である場合に用いられる場合を考慮し、base_id と target_id が一致する場合は 96 | # 距離0と考えることもできるが、一周分を距離として返す 97 | if base_id == target_id: 98 | return gval.ID_SPACE_RANGE - 1 99 | 100 | # 0をまたいだ場合に考えやすくするためにtarget_idを0にずらし、base_idを 101 | # 同じ数だけずらす 102 | slided_target_id = 0 103 | slided_base_id = base_id - target_id 104 | if(slided_base_id < 0): 105 | # マイナスの値をとった場合は値0を通り越しているので 106 | # それにあった値に置き換える 107 | slided_base_id = gval.ID_MAX + slided_base_id 108 | 109 | # 0を跨いだ場合の考慮はされているのであとは単純に値の大きな方から小さな方との差 110 | # が結果となる. ここでは slided_target_id は 0 であり、slided_base_id は必ず正の値 111 | # となっているので、 slided_base_idの値を返せばよい 112 | 113 | return slided_base_id 114 | 115 | # ID空間が環状になっていることを踏まえて base_id から後方をたどった場合の 116 | # ノード間の距離を求める 117 | # ここで後方とは、IDの値が大きくなる方向である 118 | @classmethod 119 | def calc_distance_between_nodes_right_mawari(cls, base_id : int, target_id : int) -> int: 120 | # successorが自分自身である場合に用いられる場合を考慮し、base_id と target_id が一致する場合は 121 | # 距離0と考えることもできるが、一周分を距離として返す 122 | if base_id == target_id: 123 | return gval.ID_SPACE_RANGE - 1 124 | 125 | # 0をまたいだ場合に考えやすくするためにtarget_idを0にずらし、base_idを 126 | # 同じ数だけずらす 127 | slided_target_id = target_id - base_id 128 | if(slided_target_id < 0): 129 | # マイナスの値をとった場合は値0を通り越しているので 130 | # それにあった値に置き換える 131 | slided_target_id = gval.ID_MAX + slided_target_id 132 | 133 | # 0を跨いだ場合の考慮はされているのであとは単純に値の大きな方から小さな方との差 134 | # が結果となる. ここでは slided_base_id は 0 であり、slided_target_id は必ず正の値 135 | # となっているので、 slided_base_idの値を返せばよい 136 | 137 | return slided_target_id 138 | 139 | # from_id から IDが大きくなる方向にたどった場合に、 end_id との間に 140 | # target_idが存在するか否かを bool値で返す 141 | @classmethod 142 | def exist_between_two_nodes_right_mawari(cls, from_id : int, end_id : int, target_id : int) -> bool: 143 | distance_end = ChordUtil.calc_distance_between_nodes_right_mawari(from_id, end_id) 144 | distance_target = ChordUtil.calc_distance_between_nodes_right_mawari(from_id, target_id) 145 | 146 | if distance_target < distance_end: 147 | return True 148 | else: 149 | return False 150 | 151 | # TODO: マルチプロセス安全ないしそれに近いものにする必要あり dprint 152 | @classmethod 153 | def dprint(cls, print_str : str, flush=False): 154 | print(str(datetime.datetime.now()) + "," + print_str, flush=flush) 155 | 156 | @classmethod 157 | def print_no_lf(cls, print_str : str): 158 | print(print_str, end="") 159 | 160 | @classmethod 161 | def gen_debug_str_of_node(cls, node_info : Optional['NodeInfo']) -> str: 162 | casted_info : 'NodeInfo' = cast('NodeInfo', node_info) 163 | return str(casted_info.born_id) + "," + hex(casted_info.node_id) + "," \ 164 | + ChordUtil.conv_id_to_ratio_str(casted_info.node_id) 165 | 166 | @classmethod 167 | def gen_debug_str_of_data(cls, data_id : int) -> str: 168 | return hex(data_id) + "," + ChordUtil.conv_id_to_ratio_str(data_id) 169 | 170 | # TODO: InteernalExp, DownedeExp at get_node_by_address 171 | 172 | # Attention: 取得しようとしたノードが all_node_dict に存在しないことは、そのノードが 離脱(ダウンしている状態も含) 173 | # したことを意味するため、当該状態に対応する NodeIsDownedException 例外を raise する 174 | # TODO: 実システム化する際は rpcで生存チェックをした上で、rpcで取得した情報からnode_info プロパティの値だけ適切に埋めた 175 | # ChordNodeオブジェクトを返す get_node_by_address 176 | @classmethod 177 | def get_node_by_address(cls, address : str) -> PResult[Optional['ChordNode']]: 178 | try: 179 | # with gval.lock_of_all_node_dict: 180 | ret_val = gval.all_node_dict[address] 181 | except KeyError: 182 | # join処理の途中で構築中のノード情報を取得しようとしてしまった場合に発生する 183 | # traceback.print_stack(file=sys.stdout) 184 | # print("KeyError occured", flush=True) 185 | 186 | #raise InternalControlFlowException("accessed to join operation progressing node.") 187 | return PResult.Err(None, ErrorCode.InternalControlFlowException_CODE) 188 | 189 | # except KeyError: 190 | # traceback.print_stack(file=sys.stdout) 191 | # print("KeyError occured", flush=True) 192 | # sys.exit(1) 193 | 194 | if ret_val.is_alive == False: 195 | ChordUtil.dprint("get_node_by_address_1,NODE_IS_DOWNED," + ChordUtil.gen_debug_str_of_node(ret_val.node_info)) 196 | #raise NodeIsDownedExceptiopn() 197 | return PResult.Err(None, ErrorCode.NodeIsDownedException_CODE) 198 | 199 | #return ret_val 200 | return PResult.Ok(ret_val) 201 | 202 | # TODO: InternalExp at is_node_alive 203 | 204 | # Attention: InternalControlFlowException を raiseする場合がある 205 | # TODO: 実システム化する際は アドレス指定で呼び出せる(ChordNodeオブジェクトのメソッドという形でない) 206 | # RPC化する必要がありそう。もしくはこのメソッドの呼び出し自体を無くすか。 is_node_alive 207 | @classmethod 208 | def is_node_alive(cls, address : str) -> PResult[Optional[bool]]: 209 | # try: 210 | # node_obj = ChordUtil.get_node_by_address(address) 211 | ret = ChordUtil.get_node_by_address(address) 212 | if(ret.is_ok): 213 | return PResult.Ok(True) 214 | else: 215 | if ret.err_code == ErrorCode.NodeIsDownedException_CODE: 216 | return PResult.Ok(False) 217 | else: #ret.err_code == ErrorCode.InternalControlFlowException_CODE: 218 | return PResult.Err(False, ErrorCode.InternalControlFlowException_CODE) 219 | 220 | # except NodeIsDownedExceptiopn: 221 | # return False 222 | # except InternalControlFlowException: 223 | # return False 224 | 225 | #return True 226 | 227 | 228 | # デバッグ用のメソッド 229 | # グローバル変数にあるデータIDに対応するデータがどのノードに存在するかを記録する 230 | # 本メソッドは新たにデータをstoreした際に呼び出す 231 | @classmethod 232 | def add_data_placement_info(cls, data_id : int, node_info : 'NodeInfo'): 233 | try: 234 | node_list : List['NodeInfo'] = gval.all_data_placement_dict[str(data_id)] 235 | except KeyError: 236 | node_list = [] 237 | gval.all_data_placement_dict[str(data_id)] = node_list 238 | 239 | # 既に引数で指定されたノードでの存在が記録されていた場合、同じノードのエントリが 240 | # 重複してしまうので追加せずに終了する 241 | if node_info in node_list: 242 | return 243 | 244 | node_list.append(node_info) 245 | 246 | # デバッグ用のメソッド 247 | # グローバル変数にあるデータIDに対応するデータがどのノードに存在するかを記録する 248 | # 本メソッドはデータの削除が行われた際に呼び出す 249 | @classmethod 250 | def remove_data_placement_info(cls, data_id : int, node_info : 'NodeInfo'): 251 | try: 252 | node_list : List['NodeInfo'] = gval.all_data_placement_dict[str(data_id)] 253 | except KeyError: 254 | # 本来は起きてはならないエラーだが対処のし様もないのでワーニングを出力しておく 255 | ChordUtil.dprint("remove_data_1," + ChordUtil.gen_debug_str_of_node(node_info) + "," 256 | + ChordUtil.gen_debug_str_of_data(data_id) 257 | + ",WARNING__DATA_AND_BELONGS_NODE_RERATION_MAY_BE_BROKEN") 258 | return 259 | 260 | node_list.remove(node_info) 261 | 262 | # デバッグ用のメソッド 263 | # グローバル変数にあるデータIDに対応するデータがどのノードに存在するかを出力する 264 | # 本メソッドはglobal_getが行われた際に呼び出す 265 | @classmethod 266 | def print_data_placement_info(cls, data_id : int, after_notfound_limit = False): 267 | try: 268 | node_list : List['NodeInfo'] = gval.all_data_placement_dict[str(data_id)] 269 | except KeyError: 270 | # データを持っているノードがいないか、記録のバグ 271 | ChordUtil.dprint("print_data_placement_info_1," 272 | + ChordUtil.gen_debug_str_of_data(data_id) 273 | + ",DATA_HAVING_NODE_DOES_NOT_EXIST_OR_INFORMATION_BUG") 274 | return 275 | 276 | if after_notfound_limit: 277 | additional_str = "NOT_FOUND_LIMIT_REACHED," 278 | else: 279 | additional_str = "" 280 | 281 | # ロックをとっていないので面倒な処理が頭に入っている 282 | # なお、処理中に node_list の要素が増えた場合や出力済みのデータが削除された場合は 283 | # 表示に不整合が生じるが大きな問題ではない認識 284 | list_len = len(node_list) 285 | for idx in range(0, list_len): 286 | if idx < len(node_list): 287 | ChordUtil.dprint("print_data_placement_info_INFO," + additional_str 288 | + ChordUtil.gen_debug_str_of_data(data_id) + "," 289 | + ChordUtil.gen_debug_str_of_node(node_list[idx])) 290 | 291 | @classmethod 292 | def dprint_data_storage_operations(cls, callee_node : 'NodeInfo', operation_type : str, data_id : int): 293 | if gval.ENABLE_DATA_STORE_OPERATION_DPRINT == False: 294 | return 295 | ChordUtil.dprint("dprint_data_storage_operations," + ChordUtil.gen_debug_str_of_node(callee_node) + "," 296 | + operation_type + "," + ChordUtil.gen_debug_str_of_data(data_id)) 297 | 298 | @classmethod 299 | def dprint_routing_info(cls, callee_node : 'ChordNode', calee_method : str): 300 | if gval.ENABLE_ROUTING_INFO_DPRINT == False: 301 | return 302 | ChordUtil.dprint("dprint_routing_info__PRED," + ChordUtil.gen_debug_str_of_node(callee_node.node_info) + "," 303 | + calee_method + "," + "PREDECESSOR_INFO," + str(callee_node.node_info.predecessor_info)) 304 | ChordUtil.dprint("dprint_routing_info__SUCC," +ChordUtil.gen_debug_str_of_node(callee_node.node_info) + "," + calee_method + "," 305 | + "SUCCESSOR_INFO_LIST," + str(len(callee_node.node_info.successor_info_list)) + "," 306 | + " ,| ".join([str(ninfo) for ninfo in callee_node.node_info.successor_info_list])) 307 | 308 | # @classmethod 309 | # def generic_test_ok(cls, node_info : 'NodeInfo') -> PResult[Optional['NodeInfo']]: 310 | # return PResult.Ok(node_info) 311 | # 312 | # @classmethod 313 | # def generic_test_err(cls, err_code : int) -> PResult[Optional['NodeInfo']]: 314 | # return PResult.Err(None, err_code) 315 | 316 | # # 大量のオブジェクトが紐づくNodeInfoを一気に切り替えられるようにするため、間接的にNodeInfoを 317 | # # 保持するクラスとして用いる (Listなどを間に挟むことでも同じことは可能だが、可読性が低いので避ける) 318 | # class NodeInfoPointer: 319 | # 320 | # def __init__(self, node_info : 'NodeInfo'): 321 | # self.node_info : NodeInfo = node_info 322 | 323 | # all_data_listグローバル変数に格納される形式としてのみ用いる 324 | class KeyValue: 325 | def __init__(self, key : Optional[str], value : str): 326 | self.key : Optional[str] = key 327 | self.value_data : str = value 328 | self.data_id : Optional[int] = None 329 | # keyのハッシュ値 330 | if key == None: 331 | self.data_id = None 332 | else: 333 | self.data_id = ChordUtil.hash_str_to_int(cast(str, key)) 334 | 335 | def __eq__(self, other): 336 | if not isinstance(other, KeyValue): 337 | return False 338 | return self.data_id == other.data_id 339 | 340 | # TODO: ディープコピーを取得するメソッドを定義しておきたい at DataIdAndValue 341 | @dataclasses.dataclass 342 | class DataIdAndValue: 343 | data_id : int 344 | value_data : str 345 | 346 | def __eq__(self, other): 347 | if not isinstance(other, DataIdAndValue): 348 | return False 349 | return self.data_id == other.data_id 350 | 351 | class NodeIsDownedExceptiopn(Exception): 352 | 353 | def __init__(self): 354 | super(NodeIsDownedExceptiopn, self).__init__("Accessed node seems to be downed.") 355 | 356 | class AppropriateNodeNotFoundException(Exception): 357 | 358 | def __init__(self): 359 | super(AppropriateNodeNotFoundException, self).__init__("Appropriate node is not found.") 360 | 361 | # 通常、join時に all_node_dictにノードオブジェクトが登録される前に 362 | # ノードのアドレスによる取得を試みた場合など、設計上起きてしまうことがある例外について総じて利用する 363 | class InternalControlFlowException(Exception): 364 | 365 | def __init__(self, msg_str): 366 | super(InternalControlFlowException, self).__init__(msg_str) -------------------------------------------------------------------------------- /chord_sim/modules/data_store.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | from typing import Dict, List, Optional, cast, TYPE_CHECKING 4 | 5 | from .chord_util import ChordUtil, KeyValue, DataIdAndValue, PResult, ErrorCode 6 | 7 | if TYPE_CHECKING: 8 | from .chord_node import ChordNode 9 | from .node_info import NodeInfo 10 | 11 | class DataStore: 12 | 13 | DELETED_ENTRY_MARKING_STR = "THIS_KEY_IS_DELETED" 14 | DATA_STORE_OP_DIRECT_STORE = "DIRECT_STORE" 15 | DATA_STORE_OP_DIRECT_REMOVE = "DIRECT_REMOVE" 16 | 17 | def __init__(self, existing_node : 'ChordNode'): 18 | self.existing_node : 'ChordNode' = existing_node 19 | 20 | # Keyはハッシュを通されたものなので元データの値とは異なる 21 | self.stored_data : Dict[str, DataIdAndValue] = {} 22 | 23 | # DataStoreクラスオブジェクトのデータ管理の枠組みに従った、各関連フィールドの一貫性を維持したまま 24 | # データ追加・更新処理を行うアクセサメソッド 25 | # master_node引数を指定しなかった場合は、self.existing_node.node_info をデータのマスターの情報として格納する 26 | def store_new_data(self, data_id : int, value_str : str): 27 | # ログの量が多くなりすぎるのでコメントアウトしておく 28 | # ChordUtil.dprint("store_new_data_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 29 | # + ChordUtil.gen_debug_str_of_data(data_id)) 30 | 31 | with self.existing_node.node_info.lock_of_datastore: 32 | di_entry = DataIdAndValue(data_id=data_id, value_data=value_str) 33 | 34 | # デバッグプリント 35 | ChordUtil.dprint_data_storage_operations(self.existing_node.node_info, 36 | DataStore.DATA_STORE_OP_DIRECT_STORE, 37 | data_id 38 | ) 39 | 40 | self.stored_data[str(data_id)] = di_entry 41 | # デバッグのためにグローバル変数の形で管理されているデータのロケーション情報を更新する 42 | ChordUtil.add_data_placement_info(data_id, self.existing_node.node_info) 43 | 44 | # DataStoreクラスオブジェクトのデータ管理の枠組みに従った、各関連フィールドの一貫性を維持したまま 45 | # データ削除処理を行うアクセサメソッド 46 | def remove_data(self, data_id: int): 47 | with self.existing_node.node_info.lock_of_datastore: 48 | try: 49 | del self.stored_data[str(data_id)] 50 | except KeyError: 51 | # 本来は起きてはならないエラーだが対処のし様もないのでワーニングだけ出力する 52 | ChordUtil.dprint("remove_data_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 53 | + ChordUtil.gen_debug_str_of_data(data_id) 54 | + ",WARNING__REMOVE_TARGET_DATA_NOT_EXIST") 55 | return 56 | 57 | # デバッグのためにグローバル変数の形で管理されているデータのロケーション情報を更新する 58 | ChordUtil.remove_data_placement_info(data_id, self.existing_node.node_info) 59 | # デバッグプリント 60 | ChordUtil.dprint_data_storage_operations(self.existing_node.node_info, 61 | DataStore.DATA_STORE_OP_DIRECT_REMOVE, 62 | data_id 63 | ) 64 | 65 | # 自ノードが担当ノードとなる保持データを全て返す 66 | def get_all_tantou_data(self, node_id : Optional[int] = None) -> List[DataIdAndValue]: 67 | with self.existing_node.node_info.lock_of_datastore: 68 | ChordUtil.dprint( 69 | "pass_tantou_data_for_replication_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info)) 70 | 71 | if self.existing_node.node_info.predecessor_info == None and node_id == None: 72 | ChordUtil.dprint( 73 | "pass_tantou_data_for_replication_2," + ChordUtil.gen_debug_str_of_node( 74 | self.existing_node.node_info)) 75 | return [] 76 | 77 | if node_id != None: 78 | pred_id = cast(int, node_id) 79 | else: 80 | pred_id = cast('NodeInfo', self.existing_node.node_info.predecessor_info).node_id 81 | 82 | ret_data_list : List[DataIdAndValue] = [] 83 | for key, value in self.stored_data.items(): 84 | if ChordUtil.exist_between_two_nodes_right_mawari(pred_id, self.existing_node.node_info.node_id, int(key)): 85 | ret_data_list.append(DataIdAndValue(data_id=int(key), value_data=value.value_data)) 86 | 87 | ChordUtil.dprint("pass_tantou_data_for_replication_3," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 88 | # + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info.predecessor_info) + "," 89 | + str(len(ret_data_list))) 90 | 91 | return ret_data_list 92 | 93 | # レプリカデータを受け取る 94 | # 他のノードが、保持しておいて欲しいレプリカを渡す際に呼び出される. 95 | # なお、master_node 引数と呼び出し元ノードは一致しない場合がある. 96 | # replace_allオプション引数をTrueとした場合は、指定したノードのデータを丸っと入れ替える 97 | # 返り値として、処理が完了した時点でmaster_nodeに紐づいているレプリカをいくつ保持して 98 | # いるかを返す 99 | def receive_replica(self, pass_datas : List[DataIdAndValue]): 100 | with self.existing_node.node_info.lock_of_datastore: 101 | ChordUtil.dprint("receive_replica_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 102 | + str(len(pass_datas))) 103 | 104 | for id_value in pass_datas: 105 | self.store_new_data(id_value.data_id, id_value.value_data) 106 | 107 | ChordUtil.dprint("receive_replica_2," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 108 | + str(len(pass_datas))) 109 | 110 | # 複数マスタのレプリカをまとめて受け取り格納する 111 | def store_replica_of_multi_masters(self, data_list: List[DataIdAndValue]): 112 | ChordUtil.dprint( 113 | "store_replica_of_multi_masters_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 114 | + str(len(data_list))) 115 | 116 | self.receive_replica(data_list) 117 | 118 | ChordUtil.dprint( 119 | "store_replica_of_multi_masters_2," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 120 | + str(len(data_list))) 121 | 122 | # 自身が保持しているデータのうち委譲するものを返す. 123 | # 対象となるデータは時計周りに辿った際に 引数 node_id と 自身の node_id 124 | # の間に data_id が位置するデータである. 125 | # join呼び出し時、新たに参加してきた新規ノードに、successorとなる自身が、担当から外れる 126 | # 範囲のデータの委譲を行うために、新規ノードから呼び出される形で用いられる. 127 | # rest_copy引数によってコピーを渡すだけか、完全に委譲してしまい自身のデータストアからは渡したデータを削除 128 | # するかどうか選択できる 129 | def delegate_my_tantou_data(self, node_id : int) -> List[KeyValue]: 130 | with self.existing_node.node_info.lock_of_datastore: 131 | ChordUtil.dprint("delegate_my_tantou_data_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 132 | + ChordUtil.gen_debug_str_of_data(node_id)) 133 | ret_datas : List[KeyValue] = [] 134 | tantou_data: List[DataIdAndValue] = self.get_all_tantou_data(node_id) 135 | 136 | for entry in tantou_data: 137 | # Chordネットワークを右回りにたどった時に、データの id (data_id) が呼び出し元の node_id から 138 | # 自身の node_id の間に位置する場合は、そのデータの担当は自身から変わらないため、渡すデータから 139 | # 除外する 140 | if ChordUtil.exist_between_two_nodes_right_mawari(node_id, self.existing_node.node_info.node_id, entry.data_id): 141 | ChordUtil.dprint( 142 | "delegate_my_tantou_data_2," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 143 | + ChordUtil.gen_debug_str_of_data(node_id) + "," + ChordUtil.gen_debug_str_of_data(entry.data_id)) 144 | continue 145 | 146 | # 文字列の参照をそのまま用いてしまうが、文字列はイミュータブルであるため 147 | # 問題ない 148 | item = KeyValue(None, entry.value_data) 149 | item.data_id = entry.data_id 150 | ret_datas.append(item) 151 | 152 | return ret_datas 153 | 154 | # 存在しないKeyが与えられた場合 KeyErrorがraiseされる 155 | def get(self, data_id : int) -> PResult[Optional[DataIdAndValue]]: 156 | with self.existing_node.node_info.lock_of_datastore: 157 | try: 158 | return PResult.Ok(self.stored_data[str(data_id)]) 159 | except KeyError: 160 | return PResult.Err(None, ErrorCode.KeyError_CODE) 161 | 162 | 163 | # 全ての保持しているデータを返す 164 | def get_all_data(self) -> List[DataIdAndValue]: 165 | ChordUtil.dprint("get_all_data_1," + ChordUtil.gen_debug_str_of_node( 166 | self.existing_node.node_info)) 167 | 168 | with self.existing_node.node_info.lock_of_datastore: 169 | ret_data_list: List[DataIdAndValue] = [] 170 | for key, value in self.stored_data.items(): 171 | ret_data_list.append(DataIdAndValue(data_id=int(key), value_data=value.value_data)) 172 | 173 | ChordUtil.dprint("get_all_data_2," + ChordUtil.gen_debug_str_of_node( 174 | self.existing_node.node_info) + "," 175 | + str(len(ret_data_list))) 176 | 177 | return ret_data_list 178 | 179 | # 担当データ全てのレプリカを successor_info_list内のノードに配る 180 | # 必要なロックは呼び出し元でとってある前提 181 | def distribute_replica(self): 182 | ChordUtil.dprint("distribute_replica_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info)) 183 | 184 | tantou_data_list: List[DataIdAndValue] = self.get_all_tantou_data() 185 | 186 | # レプリカを successorList内のノードに渡す(手抜きでputされたもの含めた全てを渡してしまう) 187 | for succ_info in self.existing_node.node_info.successor_info_list: 188 | # try: 189 | # succ_node: ChordNode = ChordUtil.get_node_by_address(succ_info.address_str) 190 | ret = ChordUtil.get_node_by_address(succ_info.address_str) 191 | if (ret.is_ok): 192 | succ_node : 'ChordNode' = cast('ChordNode', ret.result) 193 | else: # ret.err_code == ErrorCode.InternalControlFlowException_CODE || ret.err_code == ErrorCode.NodeIsDownedException_CODE 194 | # stabilize処理 と put処理 を経ていずれ正常な状態に 195 | # なるため、ここでは何もせずに次のノードに移る 196 | ChordUtil.dprint( 197 | "distribute_replica_2," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 198 | + ChordUtil.gen_debug_str_of_node(succ_info)) 199 | continue 200 | # except (NodeIsDownedExceptiopn, InternalControlFlowException): 201 | # # stabilize処理 と put処理 を経ていずれ正常な状態に 202 | # # なるため、ここでは何もせずに次のノードに移る 203 | # ChordUtil.dprint("distribute_replica_2," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 204 | # + ChordUtil.gen_debug_str_of_node(succ_info)) 205 | # continue 206 | 207 | # 非効率だが、putやstabilize_successorなどの度に担当データを全て渡してしまう 208 | # TODO: putやstabilize_successorが呼び出される担当データ全てのレプリカを渡すのはあまりに非効率なので、担当データのIDリストを渡して 209 | # 持っていないデータのIDのリストを返してもらい、それらのデータのみ渡すようにいずれ修正する 210 | 211 | # TODO: receive_replica call at distribute_replica 212 | succ_node.endpoints.grpc__receive_replica(tantou_data_list) 213 | 214 | ChordUtil.dprint("distribute_replica_3," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 215 | + ChordUtil.gen_debug_str_of_node(succ_info)) 216 | -------------------------------------------------------------------------------- /chord_sim/modules/endpoints.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | from typing import Dict, List, Tuple, Optional, cast, TYPE_CHECKING 4 | 5 | from .chord_util import ChordUtil, InternalControlFlowException,\ 6 | NodeIsDownedExceptiopn, DataIdAndValue, KeyValue, PResult 7 | 8 | if TYPE_CHECKING: 9 | from .chord_node import ChordNode 10 | from .node_info import NodeInfo 11 | 12 | class Endpoints: 13 | 14 | def __init__(self, existing_node : 'ChordNode'): 15 | self.existing_node = existing_node 16 | 17 | def rrpc__global_put(self, data_id : int, value_str : str) -> bool: 18 | return self.existing_node.global_put(data_id, value_str) 19 | 20 | def grpc__put(self, data_id : int, value_str : str) -> bool: 21 | return self.existing_node.put(data_id, value_str) 22 | 23 | def grpc__global_get_recover_prev(self, data_id : int) -> Tuple[str, Optional['ChordNode']]: 24 | return self.existing_node.global_get_recover_prev(data_id) 25 | 26 | def grpc__global_get_recover_succ(self, data_id: int) -> Tuple[str, Optional['ChordNode']]: 27 | return self.existing_node.global_get_recover_succ(data_id) 28 | 29 | def rrpc__global_get(self, data_id : int) -> str: 30 | return self.existing_node.global_get(data_id) 31 | 32 | def grpc__get(self, data_id : int, for_recovery = False) -> str: 33 | return self.existing_node.get(data_id, for_recovery) 34 | 35 | def grpc__global_delete(self, data_id : int) -> bool: 36 | return self.existing_node.global_delete(data_id) 37 | 38 | def grpc__pass_node_info(self) -> 'NodeInfo': 39 | return self.existing_node.pass_node_info() 40 | 41 | def grpc__get_all_tantou_data(self, node_id : Optional[int] = None) -> List[DataIdAndValue]: 42 | return self.existing_node.data_store.get_all_tantou_data(node_id) 43 | 44 | def grpc__receive_replica(self, pass_datas : List[DataIdAndValue]): 45 | return self.existing_node.data_store.receive_replica(pass_datas) 46 | 47 | def grpc__delegate_my_tantou_data(self, node_id : int) -> List[KeyValue]: 48 | return self.existing_node.data_store.delegate_my_tantou_data(node_id) 49 | 50 | def grpc__get_all_data(self) -> List[DataIdAndValue]: 51 | return self.existing_node.data_store.get_all_data() 52 | 53 | # TODO: AppropriateExp, DownedExp, InternalExp at grpc__find_successor 54 | def grpc__find_successor(self, id : int) -> PResult[Optional['ChordNode']]: 55 | return self.existing_node.router.find_successor(id) 56 | 57 | def grpc__closest_preceding_finger(self, id : int) -> 'ChordNode': 58 | return self.existing_node.router.closest_preceding_finger(id) 59 | 60 | def grpc__pass_successor_list(self) -> List['NodeInfo']: 61 | return self.existing_node.stabilizer.pass_successor_list() 62 | 63 | def grpc__pass_predecessor_info(self) -> Optional['NodeInfo']: 64 | return self.existing_node.stabilizer.pass_predecessor_info() 65 | 66 | def grpc__set_routing_infos_force(self, predecessor_info : 'NodeInfo', successor_info_0 : 'NodeInfo', ftable_enry_0 : 'NodeInfo'): 67 | return self.existing_node.stabilizer.set_routing_infos_force(predecessor_info, successor_info_0, ftable_enry_0) 68 | 69 | # TODO: InternalExp, DownedExp at grpc__stabilize_succesor_inner 70 | def grpc__stabilize_successor_inner(self) -> PResult[Optional['NodeInfo']]: 71 | return self.existing_node.stabilizer.stabilize_successor_inner() 72 | 73 | # TODO: InternalExp at grpc__check_predecessor 74 | def grpc__check_predecessor(self, node_info : 'NodeInfo') -> PResult[bool]: 75 | return self.existing_node.stabilizer.check_predecessor(node_info) 76 | 77 | # TODO: InternalExp at grpc__check_successor_list_length 78 | def grpc__check_successor_list_length(self) -> PResult[bool]: 79 | return self.existing_node.stabilizer.check_successor_list_length() 80 | 81 | # TODO: 実システムでは、ChordNodeオブジェクトが生成されたあとはこのrpcでチェック可能とする 82 | # ただし初回の呼び出しはget_node_by_addressの中で行われ、そこでのチェックを通った場合のみ 83 | # 同メソッドは ChordNodeオブジェクトを返す設計とする(通信回数が増えてしまうがそこは許容する) 84 | def grpc__is_alive(self) -> bool: 85 | raise Exception("not implemented yet") 86 | 87 | # TODO: 実システムでだけ用いる。ノード情報を取得するAPI 88 | # get_nobe_by_address内でgrpc__is_aliveでの生存チェックを通ったら 89 | # このメソッドで暫定的に生成したChordNodeオブジェクトを構築するための情報 90 | # を取得する. 内容としては NodeInfoオブジェクトのうち、successor_info_list 91 | # のみ空リストとなっているものになる見込み 92 | def grpc__get_chord_node_info(self) -> 'NodeInfo': 93 | ret_info : NodeInfo = self.existing_node.node_info.get_partial_deepcopy() 94 | if self.existing_node.node_info.predecessor_info != None: 95 | ret_info.predecessor_info = cast('NodeInfo', self.existing_node.node_info.predecessor_info).get_partial_deepcopy() 96 | return ret_info 97 | -------------------------------------------------------------------------------- /chord_sim/modules/gval.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | import threading 4 | from typing import Dict, List, TYPE_CHECKING 5 | # from readerwriterlock import rwlock 6 | 7 | if TYPE_CHECKING: 8 | from .chord_node import ChordNode 9 | from .node_info import NodeInfo 10 | from .chord_util import KeyValue 11 | from .chord_node import ChordNode 12 | 13 | ID_SPACE_BITS = 30 # 160 <- sha1での本来の値 14 | ID_SPACE_RANGE = 2**ID_SPACE_BITS # 0を含めての数である点に注意 15 | 16 | # paramaters for executing by PyPy3 on my desktop machine 17 | JOIN_INTERVAL_SEC = 1.0 #2.0 #0.9 #0.7 # 0.5 # 1 18 | PUT_INTERVAL_SEC = 0.05 #0.5 # 0.01 #0.5 # 1 19 | GET_INTERVAL_SEC = 0.05 #0.5 # 0.01 #0.5 # 1 20 | 21 | # ノード増加の勢いは 係数-1/係数 となる 22 | NODE_KILL_INTERVAL_SEC = 120.0 #20 #JOIN_INTERVAL_SEC * 10 23 | 24 | # 全ノードがstabilize_successorを実行することを1バッチとした際に 25 | # stabilize処理担当のスレッドにより呼び出されるstabilize処理を行わせる 26 | # メソッドの一回の呼び出しで何バッチが実行されるか 27 | STABILIZE_SUCCESSOR_BATCH_TIMES = 20 #10 #20 28 | # 全ノードがstabilize_finger_tableを複数回呼びされることで、finger_tableの全要素を更新 29 | # することを1バッチとした際に、stabilize処理担当のスレッドにより呼び出されるstabilize処理 30 | # を行わせるメソッドの一回の呼び出しで何バッチが実行されるか 31 | STABILIZE_FTABLE_BATCH_TIMES = 2 #1 32 | 33 | # 一時的にこれより短くなる場合もある 34 | SUCCESSOR_LIST_NORMAL_LEN = 3 35 | 36 | # # 160bit符号なし整数の最大値 37 | # # Chordネットワーク上のID空間の上限 38 | # ID_MAX = 2**ID_SPACE_BITS - 1 39 | 40 | # 30bit符号なし整数の最大値 41 | # Chordネットワーク上のID空間の上限 42 | # TODO: 検証時の実行時間短縮のためにハッシュ関数で求めた値の代わりに乱数 43 | # を用いているため bit数 を少なくしている 44 | ID_MAX = ID_SPACE_RANGE - 1 45 | 46 | KEEP_NODE_NUM = 50 #100 47 | NODE_NUM_MAX = 10000 48 | 49 | LOCK_ACQUIRE_TIMEOUT = 3 #10 50 | 51 | # プロセス内の全てのデータへのアクセスに対するロック変数 52 | # 実装していく過程で細粒度のロックに対応できていない場合や、デバッグ用途に用いる 53 | lock_of_all_data = threading.Lock() 54 | 55 | # TODO: all_node_dictとall_data_listのロックはRustの該当するコレクションがスレッドセーフか 56 | # 確認してから必要なところだけに絞る必要あり(例えば、readアクセスでも結果にセンシティブなところ以外は不要ではないかなど) 57 | 58 | # アドレス文字列をキーとしてとり、対応するノードのChordNodeオブジェクトを返すハッシュ 59 | # IPアドレスが分かれば、対応するノードと通信できることと対応している 60 | all_node_dict : Dict[str, 'ChordNode'] = {} 61 | lock_of_all_node_dict = threading.Lock() 62 | 63 | # DHT上で保持されている全てのデータが保持されているリスト 64 | # KeyValueオブジェクトを要素として持つ 65 | # 全てのノードはputの際はDHTにデータをputするのとは別にこのリストにデータを追加し、 66 | # getする際はDHTに対してgetを発行するためのデータをこのリストからランダム 67 | # に選び、そのkeyを用いて探索を行う. また value も保持しておき、取得できた内容と 68 | # 照らし合わせられるようにする 69 | all_data_list : List['KeyValue'] = [] 70 | lock_of_all_data_list = threading.Lock() 71 | 72 | # 検証を分かりやすくするために何ノード目として生成されたか 73 | # のデバッグ用IDを持たせるためのカウンタ 74 | already_born_node_num = 0 75 | 76 | is_network_constructed = False 77 | 78 | # デバッグ用の変数群 79 | global_get_retry_cnt = 0 80 | GLOBAL_GET_RETRY_CNT_LIMIT_TO_DEBEUG_PRINT = 30 81 | 82 | # マスターデータとレプリカの区別なく、データIDをKeyに、当該IDに対応するデータを 83 | # 保持しているノードのリストを得られる dict 84 | all_data_placement_dict : Dict[str, List['NodeInfo']] = {} 85 | 86 | # 既に発行したputの回数 87 | already_issued_put_cnt = 0 88 | 89 | # stabilize_successorのループの回せる回数の上限 90 | TRYING_GET_SUCC_TIMES_LIMIT = SUCCESSOR_LIST_NORMAL_LEN * 5 91 | 92 | # # デバッグ用のフラグ 93 | # # killスレッドがWriter、他のスレッドはReaderとしてロックを取得する 94 | # kill_thread_lock_factory = rwlock.RWLockFairD() 95 | # kill_thread_write_lock = kill_thread_lock_factory.gen_wlock() 96 | # kill_thread_read_lock = kill_thread_lock_factory.gen_rlock() 97 | 98 | STABILIZE_THREAD_NUM = 3 #10 99 | 100 | ENABLE_DATA_STORE_OPERATION_DPRINT = False 101 | ENABLE_ROUTING_INFO_DPRINT = False 102 | 103 | # partial_join_opが実行されることを待っているノードが存在するか否か 104 | # join と partial_join_op の間で、該当ノードがkillされることを避けるために用いる 105 | is_waiting_partial_join_op_exists = False -------------------------------------------------------------------------------- /chord_sim/modules/node_info.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | import copy 4 | from typing import List, Optional 5 | 6 | from . import gval 7 | from .chord_util import ChordUtil 8 | import threading 9 | 10 | # メモ: オブジェクトをdictのキーとして使用可能としてある 11 | class NodeInfo: 12 | 13 | def __init__(self): 14 | self.node_id: int = -1 15 | self.address_str: str = "" 16 | 17 | # デバッグ用のID 18 | # 何ノード目として生成されたかの値 19 | # TODO: 実システムでは開発中(というか、スクリプトで順にノード起動していくような形)でないと 20 | # 利用できないことは念頭おいて置く必要あり NodeInfo#born_id 21 | self.born_id: int = -1 22 | 23 | # 以下の2つはNodeInfoオブジェクトを保持. 24 | # ある時点で取得したものが保持されており、変化する場合のあるフィールド 25 | # の内容は最新の内容となっているとは限らないため注意が必要. 26 | # そのような情報が必要な場合はChordNodeオブジェクトから参照し、 27 | # 必要であれば、その際に下のフィールドにdeepcopyを設定しなおさ 28 | # なければならない. 29 | 30 | # 状況に応じて伸縮するが、インデックス0には必ず 非None な要素が入っている 31 | # ように制御する 32 | self.successor_info_list: List[NodeInfo] = [] 33 | # join後はNoneになることのないように制御される 34 | self.predecessor_info: Optional[NodeInfo] = None 35 | 36 | # predecessor_info と successor_info_list のそれぞれに対応する 37 | # ロック変数(re-entrantロック) 38 | self.lock_of_pred_info : threading.RLock = threading.RLock() 39 | self.lock_of_succ_infos : threading.RLock = threading.RLock() 40 | 41 | # stored_data, master2data_idx、master_node_dict 全てのフィールドに対する 42 | # ロック変数(re-entrantロック) 43 | self.lock_of_datastore : threading.RLock = threading.RLock() 44 | 45 | # NodeInfoオブジェクトを要素として持つリスト 46 | # インデックスの小さい方から狭い範囲が格納される形で保持する 47 | # sha1で生成されるハッシュ値は160bit符号無し整数であるため要素数は160となる 48 | # TODO: 現在は ID_SPACE_BITS が検証時の実行時間の短縮のため30となっている 49 | self.finger_table: List[Optional[NodeInfo]] = [None] * gval.ID_SPACE_BITS 50 | 51 | # 単純にdeepcopyするとチェーン構造になっているものが全てコピーされてしまう 52 | # ため、そこの考慮を行い、また、finger_tableはコピーしない形での deepcopy 53 | # を返す. 54 | # 上述の考慮により、コピーした NodeInfoオブジェクト の successor_infoと 55 | # predecessor_infoは deepcopy の対象ではあるが、それらの中の同名のフィールド 56 | # にはNoneが設定される. これにより、あるノードがコピーされた NodeInfo を保持 57 | # した場合、predecessor や successorは辿ることができるが、その先は辿ることが 58 | # 直接的にはできないことになる(predecessor や successorの ChordNodeオブジェクト 59 | # を引いてやれば可能) 60 | # 用途としては、あるノードの node_info を他のノードが取得し保持する際に利用される 61 | # ことを想定して実装されている. 62 | def get_partial_deepcopy(self) -> 'NodeInfo': 63 | ret_node_info: NodeInfo = NodeInfo() 64 | 65 | ret_node_info.node_id = copy.copy(self.node_id) 66 | ret_node_info.address_str = copy.copy(self.address_str) 67 | ret_node_info.born_id = copy.copy(self.born_id) 68 | ret_node_info.successor_info_list = [] 69 | ret_node_info.predecessor_info = None 70 | 71 | # ロック関連のフィールドは本メソッドでコピーすることで生まれた 72 | # オブジェクトにおいて利用されることがあったとしても、ロックの 73 | # 対象は上記でコピーしているオブジェクトではなく、フィールドそのもの 74 | # であるため、コピーの必要はない 75 | 76 | return ret_node_info 77 | 78 | def __eq__(self, other): 79 | if not isinstance(other, NodeInfo): 80 | return False 81 | return self.node_id == other.node_id 82 | 83 | def __hash__(self): 84 | return self.node_id 85 | 86 | def __str__(self): 87 | return ChordUtil.gen_debug_str_of_node(self) -------------------------------------------------------------------------------- /chord_sim/modules/router.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | from typing import Dict, List, Optional, cast, TYPE_CHECKING 4 | 5 | import modules.gval as gval 6 | from .chord_util import ChordUtil, NodeIsDownedExceptiopn, \ 7 | AppropriateNodeNotFoundException, InternalControlFlowException, PResult, ErrorCode 8 | 9 | if TYPE_CHECKING: 10 | from .node_info import NodeInfo 11 | from .chord_node import ChordNode 12 | 13 | class Router: 14 | 15 | def __init__(self, existing_node : 'ChordNode'): 16 | self.existing_node : 'ChordNode' = existing_node 17 | 18 | # id(int)で識別されるデータを担当するノードの名前解決を行う 19 | # Attention: 適切な担当ノードを得ることができなかった場合、FindNodeFailedExceptionがraiseされる 20 | # TODO: AppropriateExp, DownedExp, InternalExp at find_successor 21 | def find_successor(self, id : int) -> PResult[Optional['ChordNode']]: 22 | # TODO: ここでのロックをはじめとしてRust実装ではロック対象を更新するか否かでRWロックを使い分けるようにする. at find_successor 23 | # そうでないと、少なくともglobal_xxxの呼び出しを同一ノードもしくは、いくつかのノードに行うような運用でクエリが並列に 24 | # 動作せず、パフォーマンスが出ないはず 25 | if self.existing_node.node_info.lock_of_succ_infos.acquire(timeout=gval.LOCK_ACQUIRE_TIMEOUT) == False: 26 | # 失敗させる 27 | ChordUtil.dprint("find_successor_0," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 28 | + "LOCK_ACQUIRE_TIMEOUT") 29 | #raise InternalControlFlowException("gettting lock of successor_linfo_list is timedout.") 30 | return PResult.Err(None, ErrorCode.InternalControlFlowException_CODE) 31 | 32 | if self.existing_node.is_alive == False: 33 | # 処理の合間でkillされてしまっていた場合の考慮 34 | # 何もしないで終了する 35 | self.existing_node.node_info.lock_of_succ_infos.release() 36 | ChordUtil.dprint("find_successor_0_5," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 37 | + "REQUEST_RECEIVED_BUT_I_AM_ALREADY_DEAD") 38 | #raise NodeIsDownedExceptiopn() 39 | return PResult.Err(None, ErrorCode.NodeIsDownedException_CODE) 40 | 41 | try: 42 | ChordUtil.dprint("find_successor_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 43 | + ChordUtil.gen_debug_str_of_data(id)) 44 | 45 | n_dash = self.find_predecessor(id) 46 | if n_dash == None: 47 | ChordUtil.dprint("find_successor_2," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 48 | + ChordUtil.gen_debug_str_of_data(id)) 49 | #raise AppropriateNodeNotFoundException() 50 | return PResult.Err(None, ErrorCode.AppropriateNodeNotFoundException_CODE) 51 | 52 | # TODO: x direct access to node_info of n_dash at find_successor 53 | ChordUtil.dprint("find_successor_3," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 54 | + ChordUtil.gen_debug_str_of_node(n_dash.node_info) + "," 55 | + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info.successor_info_list[0]) + "," 56 | + ChordUtil.gen_debug_str_of_data(id)) 57 | 58 | # 取得しようとしたノードがダウンしていた場合 AppropriateNodeNotFoundException が raise される 59 | # TODO: direct access to successor_info_list of n_dash at find_successor 60 | #n_dash_successor : 'ChordNode' = ChordUtil.get_node_by_address(n_dash.node_info.successor_info_list[0].address_str) 61 | ret = ChordUtil.get_node_by_address(n_dash.node_info.successor_info_list[0].address_str) 62 | if(ret.is_ok): 63 | n_dash_successor : 'ChordNode' = cast('ChordNode', ret.result) 64 | return PResult.Ok(n_dash_successor) 65 | else: # ret.err_code == ErrorCode.InternalControlFlowException_CODE || ret.err_code == ErrorCode.NodeIsDownedException_CODE 66 | # ここでは何も対処しない 67 | ChordUtil.dprint("find_successor_4,FOUND_NODE_IS_DOWNED," + ChordUtil.gen_debug_str_of_node( 68 | self.existing_node.node_info) + "," 69 | + ChordUtil.gen_debug_str_of_data(id)) 70 | return PResult.Err(None, ErrorCode.AppropriateNodeNotFoundException_CODE) 71 | finally: 72 | self.existing_node.node_info.lock_of_succ_infos.release() 73 | 74 | # id(int) の前で一番近い位置に存在するノードを探索する 75 | def find_predecessor(self, id: int) -> 'ChordNode': 76 | ChordUtil.dprint("find_predecessor_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info)) 77 | 78 | n_dash : 'ChordNode' = self.existing_node 79 | 80 | if self.existing_node.node_info.lock_of_succ_infos.acquire(timeout=gval.LOCK_ACQUIRE_TIMEOUT) == False: 81 | # 最初の n_dash を返してしまい、find_predecessorは失敗したと判断させる 82 | ChordUtil.dprint("find_predecessor_1_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 83 | + "LOCK_ACQUIRE_TIMEOUT") 84 | return n_dash 85 | try: 86 | # n_dash と n_dashのsuccessorの 間に id が位置するような n_dash を見つけたら、ループを終了し n_dash を return する 87 | # TODO: direct access to node_id and successor_info_list of n_dash at find_predecessor 88 | while not ChordUtil.exist_between_two_nodes_right_mawari(n_dash.node_info.node_id, n_dash.node_info.successor_info_list[0].node_id, id): 89 | # TODO: x direct access to node_info of n_dash at find_predecessor 90 | ChordUtil.dprint("find_predecessor_2," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 91 | + ChordUtil.gen_debug_str_of_node(n_dash.node_info)) 92 | # TODO: closest_preceding_finger call at find_predecessor 93 | n_dash_found = n_dash.endpoints.grpc__closest_preceding_finger(id) 94 | 95 | # TODO: x direct access to node_info of n_dash_found and n_dash at find_predecessor 96 | if n_dash_found.node_info.node_id == n_dash.node_info.node_id: 97 | # 見つかったノードが、n_dash と同じで、変わらなかった場合 98 | # 同じを経路表を用いて探索することになり、結果は同じになり無限ループと 99 | # なってしまうため、探索は継続せず、探索結果として n_dash (= n_dash_found) を返す 100 | # TODO: x direct access to node_info of n_dash at find_predecessor 101 | ChordUtil.dprint("find_predecessor_3," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 102 | + ChordUtil.gen_debug_str_of_node(n_dash.node_info)) 103 | return n_dash_found 104 | 105 | # closelst_preceding_finger は id を通り越してしまったノードは返さない 106 | # という前提の元で以下のチェックを行う 107 | # TODO: x direct access to node_info of n_dash at find_predecessor 108 | distance_old = ChordUtil.calc_distance_between_nodes_right_mawari(self.existing_node.node_info.node_id, n_dash.node_info.node_id) 109 | # TODO: x direct access to node_info of n_dash_found at find_predecessor 110 | distance_found = ChordUtil.calc_distance_between_nodes_right_mawari(self.existing_node.node_info.node_id, n_dash_found.node_info.node_id) 111 | distance_data_id = ChordUtil.calc_distance_between_nodes_right_mawari(self.existing_node.node_info.node_id, id) 112 | if distance_found < distance_old and not (distance_old >= distance_data_id): 113 | # 探索を続けていくと n_dash は id に近付いていくはずであり、それは上記の前提を踏まえると 114 | # 自ノードからはより遠い位置の値になっていくということのはずである 115 | # 従って、そうなっていなかった場合は、繰り返しを継続しても意味が無く、最悪、無限ループになってしまう 116 | # 可能性があるため、探索を打ち切り、探索結果は古いn_dashを返す. 117 | # ただし、古い n_dash が 一回目の探索の場合 self であり、同じ node_idの距離は ID_SPACE_RANGE となるようにしている 118 | # ため、上記の条件が常に成り立ってしまう. 従って、その場合は例外とする(n_dashが更新される場合は、更新されたn_dashのnode_idが 119 | # 探索対象のデータのid を通り越すことは無い) 120 | 121 | # TODO: x direct access to node_info of n_dash at find_predecessor 122 | ChordUtil.dprint("find_predecessor_4," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 123 | + ChordUtil.gen_debug_str_of_node(n_dash.node_info)) 124 | 125 | return n_dash 126 | 127 | # TODO: x direct access to node_info of n_dash and n_dash_found at find_predecessor 128 | ChordUtil.dprint("find_predecessor_5_n_dash_updated," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 129 | + ChordUtil.gen_debug_str_of_node(n_dash.node_info) + "->" 130 | + ChordUtil.gen_debug_str_of_node(n_dash_found.node_info)) 131 | 132 | # チェックの結果問題ないので n_dashを closest_preceding_fingerで探索して得た 133 | # ノード情報 n_dash_foundに置き換える 134 | n_dash = n_dash_found 135 | finally: 136 | self.existing_node.node_info.lock_of_succ_infos.release() 137 | 138 | return n_dash 139 | 140 | # 自身の持つ経路情報をもとに, id から前方向に一番近いノードの情報を返す 141 | def closest_preceding_finger(self, id : int) -> 'ChordNode': 142 | # 範囲の広いエントリから探索していく 143 | # finger_tableはインデックスが小さい方から大きい方に、範囲が大きくなっていく 144 | # ように構成されているため、リバースしてインデックスの大きな方から小さい方へ 145 | # 順に見ていくようにする 146 | for node_info in reversed(self.existing_node.node_info.finger_table): 147 | # 埋まっていないエントリも存在し得る 148 | if node_info == None: 149 | ChordUtil.dprint("closest_preceding_finger_0," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info)) 150 | continue 151 | 152 | casted_node_info = cast('NodeInfo', node_info) 153 | 154 | ChordUtil.dprint("closest_preceding_finger_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 155 | + ChordUtil.gen_debug_str_of_node(casted_node_info)) 156 | 157 | # テーブル内のエントリが保持しているノードのIDが自身のIDと探索対象のIDの間にあれば 158 | # それを返す 159 | # (大きな範囲を見た場合、探索対象のIDが自身のIDとエントリが保持しているノードのIDの中に含まれて 160 | # しまっている可能性が高く、エントリが保持しているノードが、探索対象のIDを飛び越してしまっている 161 | # 可能性が高いということになる。そこで探索範囲を狭めていって、飛び越さない範囲で一番近いノードを 162 | # 見つけるという処理になっていると思われる) 163 | # #if self.existing_node.node_info.node_id < entry.node_id and entry.node_id <= id: 164 | if ChordUtil.exist_between_two_nodes_right_mawari(self.existing_node.node_info.node_id, id, casted_node_info.node_id): 165 | ChordUtil.dprint("closest_preceding_finger_2," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 166 | + ChordUtil.gen_debug_str_of_node(casted_node_info)) 167 | # try: 168 | # casted_node : 'ChordNode' = ChordUtil.get_node_by_address(casted_node_info.address_str) 169 | # return casted_node 170 | 171 | ret = ChordUtil.get_node_by_address(casted_node_info.address_str) 172 | if (ret.is_ok): 173 | casted_node : 'ChordNode' = cast('ChordNode', ret.result) 174 | return casted_node 175 | else: # ret.err_code == ErrorCode.InternalControlFlowException_CODE || ret.err_code == ErrorCode.NodeIsDownedException_CODE 176 | # ここでは何も対処しない 177 | continue 178 | # except (InternalControlFlowException, NodeIsDownedExceptiopn): 179 | # # ここでは何も対処しない 180 | # continue 181 | 182 | ChordUtil.dprint("closest_preceding_finger_3") 183 | 184 | # どんなに範囲を狭めても探索対象のIDを超えてしまうノードしか存在しなかった場合 185 | # 自身の知っている情報の中で対象を飛び越さない範囲で一番近いノードは自身という 186 | # ことになる 187 | return self.existing_node 188 | -------------------------------------------------------------------------------- /chord_sim/modules/taskqueue.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | from typing import Dict, List, Optional, cast, TYPE_CHECKING 4 | 5 | from .chord_util import ChordUtil, InternalControlFlowException, NodeIsDownedExceptiopn 6 | 7 | if TYPE_CHECKING: 8 | from .chord_node import ChordNode 9 | 10 | class TaskQueue: 11 | JOIN_PARTIAL = "join_partial" 12 | 13 | def __init__(self, existing_node : 'ChordNode'): 14 | self.tqueue : List[str] = [] 15 | self.existing_node = existing_node 16 | 17 | def append_task(self, task_code : str): 18 | self.tqueue.append(task_code) 19 | 20 | # キュー内の最初のタスクを実行する 21 | # 処理が失敗した場合は先頭に戻す 22 | def exec_first(self): 23 | if len(self.tqueue) > 0: 24 | ChordUtil.dprint("exec_first_0," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," + str(self.tqueue)) 25 | task_code : str = self.tqueue.pop() 26 | if task_code == TaskQueue.JOIN_PARTIAL: 27 | # try: 28 | #self.existing_node.stabilizer.partial_join_op() 29 | ret = self.existing_node.stabilizer.partial_join_op() 30 | if (ret.is_ok): 31 | pass 32 | else: # ret.err_code == ErrorCode.InternalControlFlowException_CODE 33 | # 実行に失敗したため再実行すべく先頭に戻す 34 | self.tqueue.insert(0, task_code) 35 | ChordUtil.dprint( 36 | "exec_first_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 37 | + "INTERNAL_CONTROL_FLOW_EXCEPTION_OCCURED") 38 | 39 | # except (InternalControlFlowException, NodeIsDownedExceptiopn): 40 | # # 実行に失敗したため再実行すべく先頭に戻す 41 | # self.tqueue.insert(0, task_code) 42 | # ChordUtil.dprint("exec_first_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 43 | # + "INTERNAL_CONTROL_FLOW_EXCEPTION_OCCURED") 44 | -------------------------------------------------------------------------------- /chord_sim/requirements.txt: -------------------------------------------------------------------------------- 1 | cffi==1.14.3 2 | greenlet==0.4.13 3 | readerwriterlock==1.0.8 4 | readline==6.2.4.1 5 | typing-extensions==3.7.4.3 6 | -------------------------------------------------------------------------------- /chord_sim_rust/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "arrayref" 7 | version = "0.3.6" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" 10 | 11 | [[package]] 12 | name = "arrayvec" 13 | version = "0.5.2" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" 16 | 17 | [[package]] 18 | name = "autocfg" 19 | version = "1.0.1" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 22 | 23 | [[package]] 24 | name = "base64" 25 | version = "0.13.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 28 | 29 | [[package]] 30 | name = "bitflags" 31 | version = "1.2.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 34 | 35 | [[package]] 36 | name = "blake2b_simd" 37 | version = "0.5.11" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" 40 | dependencies = [ 41 | "arrayref", 42 | "arrayvec", 43 | "constant_time_eq", 44 | ] 45 | 46 | [[package]] 47 | name = "byteorder" 48 | version = "1.4.3" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 51 | 52 | [[package]] 53 | name = "cfg-if" 54 | version = "1.0.0" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 57 | 58 | [[package]] 59 | name = "chord_sim_rust" 60 | version = "0.1.0" 61 | dependencies = [ 62 | "chrono", 63 | "clippy", 64 | "lazy_static", 65 | "parking_lot", 66 | "rand", 67 | ] 68 | 69 | [[package]] 70 | name = "chrono" 71 | version = "0.4.19" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 74 | dependencies = [ 75 | "libc", 76 | "num-integer", 77 | "num-traits", 78 | "time", 79 | "winapi", 80 | ] 81 | 82 | [[package]] 83 | name = "clippy" 84 | version = "0.0.302" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "d911ee15579a3f50880d8c1d59ef6e79f9533127a3bd342462f5d584f5e8c294" 87 | dependencies = [ 88 | "term", 89 | ] 90 | 91 | [[package]] 92 | name = "constant_time_eq" 93 | version = "0.1.5" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" 96 | 97 | [[package]] 98 | name = "crossbeam-utils" 99 | version = "0.8.4" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278" 102 | dependencies = [ 103 | "autocfg", 104 | "cfg-if", 105 | "lazy_static", 106 | ] 107 | 108 | [[package]] 109 | name = "dirs" 110 | version = "1.0.5" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" 113 | dependencies = [ 114 | "libc", 115 | "redox_users", 116 | "winapi", 117 | ] 118 | 119 | [[package]] 120 | name = "getrandom" 121 | version = "0.1.16" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" 124 | dependencies = [ 125 | "cfg-if", 126 | "libc", 127 | "wasi 0.9.0+wasi-snapshot-preview1", 128 | ] 129 | 130 | [[package]] 131 | name = "getrandom" 132 | version = "0.2.2" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" 135 | dependencies = [ 136 | "cfg-if", 137 | "libc", 138 | "wasi 0.10.2+wasi-snapshot-preview1", 139 | ] 140 | 141 | [[package]] 142 | name = "instant" 143 | version = "0.1.9" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" 146 | dependencies = [ 147 | "cfg-if", 148 | ] 149 | 150 | [[package]] 151 | name = "lazy_static" 152 | version = "1.4.0" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 155 | 156 | [[package]] 157 | name = "libc" 158 | version = "0.2.94" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" 161 | 162 | [[package]] 163 | name = "lock_api" 164 | version = "0.4.4" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" 167 | dependencies = [ 168 | "scopeguard", 169 | ] 170 | 171 | [[package]] 172 | name = "num-integer" 173 | version = "0.1.44" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 176 | dependencies = [ 177 | "autocfg", 178 | "num-traits", 179 | ] 180 | 181 | [[package]] 182 | name = "num-traits" 183 | version = "0.2.14" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 186 | dependencies = [ 187 | "autocfg", 188 | ] 189 | 190 | [[package]] 191 | name = "parking_lot" 192 | version = "0.11.1" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" 195 | dependencies = [ 196 | "instant", 197 | "lock_api", 198 | "parking_lot_core", 199 | ] 200 | 201 | [[package]] 202 | name = "parking_lot_core" 203 | version = "0.8.3" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" 206 | dependencies = [ 207 | "cfg-if", 208 | "instant", 209 | "libc", 210 | "redox_syscall 0.2.8", 211 | "smallvec", 212 | "winapi", 213 | ] 214 | 215 | [[package]] 216 | name = "ppv-lite86" 217 | version = "0.2.10" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 220 | 221 | [[package]] 222 | name = "rand" 223 | version = "0.8.3" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" 226 | dependencies = [ 227 | "libc", 228 | "rand_chacha", 229 | "rand_core", 230 | "rand_hc", 231 | ] 232 | 233 | [[package]] 234 | name = "rand_chacha" 235 | version = "0.3.0" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" 238 | dependencies = [ 239 | "ppv-lite86", 240 | "rand_core", 241 | ] 242 | 243 | [[package]] 244 | name = "rand_core" 245 | version = "0.6.2" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" 248 | dependencies = [ 249 | "getrandom 0.2.2", 250 | ] 251 | 252 | [[package]] 253 | name = "rand_hc" 254 | version = "0.3.0" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" 257 | dependencies = [ 258 | "rand_core", 259 | ] 260 | 261 | [[package]] 262 | name = "redox_syscall" 263 | version = "0.1.57" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 266 | 267 | [[package]] 268 | name = "redox_syscall" 269 | version = "0.2.8" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" 272 | dependencies = [ 273 | "bitflags", 274 | ] 275 | 276 | [[package]] 277 | name = "redox_users" 278 | version = "0.3.5" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" 281 | dependencies = [ 282 | "getrandom 0.1.16", 283 | "redox_syscall 0.1.57", 284 | "rust-argon2", 285 | ] 286 | 287 | [[package]] 288 | name = "rust-argon2" 289 | version = "0.8.3" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" 292 | dependencies = [ 293 | "base64", 294 | "blake2b_simd", 295 | "constant_time_eq", 296 | "crossbeam-utils", 297 | ] 298 | 299 | [[package]] 300 | name = "scopeguard" 301 | version = "1.1.0" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 304 | 305 | [[package]] 306 | name = "smallvec" 307 | version = "1.6.1" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" 310 | 311 | [[package]] 312 | name = "term" 313 | version = "0.5.2" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" 316 | dependencies = [ 317 | "byteorder", 318 | "dirs", 319 | "winapi", 320 | ] 321 | 322 | [[package]] 323 | name = "time" 324 | version = "0.1.43" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" 327 | dependencies = [ 328 | "libc", 329 | "winapi", 330 | ] 331 | 332 | [[package]] 333 | name = "wasi" 334 | version = "0.9.0+wasi-snapshot-preview1" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 337 | 338 | [[package]] 339 | name = "wasi" 340 | version = "0.10.2+wasi-snapshot-preview1" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 343 | 344 | [[package]] 345 | name = "winapi" 346 | version = "0.3.9" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 349 | dependencies = [ 350 | "winapi-i686-pc-windows-gnu", 351 | "winapi-x86_64-pc-windows-gnu", 352 | ] 353 | 354 | [[package]] 355 | name = "winapi-i686-pc-windows-gnu" 356 | version = "0.4.0" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 359 | 360 | [[package]] 361 | name = "winapi-x86_64-pc-windows-gnu" 362 | version = "0.4.0" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 365 | -------------------------------------------------------------------------------- /chord_sim_rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chord_sim_rust" 3 | version = "0.1.0" 4 | authors = ["Ryo Kanbayashi "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | #rocket = "0.4.5" 11 | #rocket_contrib = { version = "0.4.5", features = ["json"] } 12 | lazy_static = "1.4.0" 13 | parking_lot = "0.11" 14 | clippy = { version = "*", optional = true } 15 | rand = "0.8.3" 16 | chrono = "0.4" -------------------------------------------------------------------------------- /chord_sim_rust/src/data_store.rs: -------------------------------------------------------------------------------- 1 | /* 2 | # coding:utf-8 3 | 4 | from .chord_util import ChordUtil, KeyValue, DataIdAndValue, PResult, ErrorCode 5 | 6 | class DataStore: 7 | 8 | DELETED_ENTRY_MARKING_STR = "THIS_KEY_IS_DELETED" 9 | DATA_STORE_OP_DIRECT_STORE = "DIRECT_STORE" 10 | DATA_STORE_OP_DIRECT_REMOVE = "DIRECT_REMOVE" 11 | 12 | def __init__(self, existing_node : 'ChordNode'): 13 | self.existing_node : 'ChordNode' = existing_node 14 | 15 | # Keyはハッシュを通されたものなので元データの値とは異なる 16 | self.stored_data : Dict[str, DataIdAndValue] = {} 17 | 18 | # DataStoreクラスオブジェクトのデータ管理の枠組みに従った、各関連フィールドの一貫性を維持したまま 19 | # データ追加・更新処理を行うアクセサメソッド 20 | # master_node引数を指定しなかった場合は、self.existing_node.node_info をデータのマスターの情報として格納する 21 | def store_new_data(self, data_id : int, value_str : str): 22 | # ログの量が多くなりすぎるのでコメントアウトしておく 23 | # ChordUtil.dprint("store_new_data_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 24 | # + ChordUtil.gen_debug_str_of_data(data_id)) 25 | 26 | with self.existing_node.node_info.lock_of_datastore: 27 | di_entry = DataIdAndValue(data_id=data_id, value_data=value_str) 28 | 29 | # デバッグプリント 30 | ChordUtil.dprint_data_storage_operations(self.existing_node.node_info, 31 | DataStore.DATA_STORE_OP_DIRECT_STORE, 32 | data_id 33 | ) 34 | 35 | self.stored_data[str(data_id)] = di_entry 36 | # デバッグのためにグローバル変数の形で管理されているデータのロケーション情報を更新する 37 | ChordUtil.add_data_placement_info(data_id, self.existing_node.node_info) 38 | 39 | # DataStoreクラスオブジェクトのデータ管理の枠組みに従った、各関連フィールドの一貫性を維持したまま 40 | # データ削除処理を行うアクセサメソッド 41 | def remove_data(self, data_id: int): 42 | with self.existing_node.node_info.lock_of_datastore: 43 | try: 44 | del self.stored_data[str(data_id)] 45 | except KeyError: 46 | # 本来は起きてはならないエラーだが対処のし様もないのでワーニングだけ出力する 47 | ChordUtil.dprint("remove_data_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 48 | + ChordUtil.gen_debug_str_of_data(data_id) 49 | + ",WARNING__REMOVE_TARGET_DATA_NOT_EXIST") 50 | return 51 | 52 | # デバッグのためにグローバル変数の形で管理されているデータのロケーション情報を更新する 53 | ChordUtil.remove_data_placement_info(data_id, self.existing_node.node_info) 54 | # デバッグプリント 55 | ChordUtil.dprint_data_storage_operations(self.existing_node.node_info, 56 | DataStore.DATA_STORE_OP_DIRECT_REMOVE, 57 | data_id 58 | ) 59 | 60 | # 自ノードが担当ノードとなる保持データを全て返す 61 | def get_all_tantou_data(self, node_id : Optional[int] = None) -> List[DataIdAndValue]: 62 | with self.existing_node.node_info.lock_of_datastore: 63 | ChordUtil.dprint( 64 | "pass_tantou_data_for_replication_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info)) 65 | 66 | if self.existing_node.node_info.predecessor_info == None and node_id == None: 67 | ChordUtil.dprint( 68 | "pass_tantou_data_for_replication_2," + ChordUtil.gen_debug_str_of_node( 69 | self.existing_node.node_info)) 70 | return [] 71 | 72 | if node_id != None: 73 | pred_id = cast(int, node_id) 74 | else: 75 | pred_id = cast('NodeInfo', self.existing_node.node_info.predecessor_info).node_id 76 | 77 | ret_data_list : List[DataIdAndValue] = [] 78 | for key, value in self.stored_data.items(): 79 | if ChordUtil.exist_between_two_nodes_right_mawari(pred_id, self.existing_node.node_info.node_id, int(key)): 80 | ret_data_list.append(DataIdAndValue(data_id=int(key), value_data=value.value_data)) 81 | 82 | ChordUtil.dprint("pass_tantou_data_for_replication_3," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 83 | # + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info.predecessor_info) + "," 84 | + str(len(ret_data_list))) 85 | 86 | return ret_data_list 87 | 88 | # レプリカデータを受け取る 89 | # 他のノードが、保持しておいて欲しいレプリカを渡す際に呼び出される. 90 | # なお、master_node 引数と呼び出し元ノードは一致しない場合がある. 91 | # replace_allオプション引数をTrueとした場合は、指定したノードのデータを丸っと入れ替える 92 | # 返り値として、処理が完了した時点でmaster_nodeに紐づいているレプリカをいくつ保持して 93 | # いるかを返す 94 | def receive_replica(self, pass_datas : List[DataIdAndValue]): 95 | with self.existing_node.node_info.lock_of_datastore: 96 | ChordUtil.dprint("receive_replica_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 97 | + str(len(pass_datas))) 98 | 99 | for id_value in pass_datas: 100 | self.store_new_data(id_value.data_id, id_value.value_data) 101 | 102 | ChordUtil.dprint("receive_replica_2," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 103 | + str(len(pass_datas))) 104 | 105 | # 複数マスタのレプリカをまとめて受け取り格納する 106 | def store_replica_of_multi_masters(self, data_list: List[DataIdAndValue]): 107 | ChordUtil.dprint( 108 | "store_replica_of_multi_masters_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 109 | + str(len(data_list))) 110 | 111 | self.receive_replica(data_list) 112 | 113 | ChordUtil.dprint( 114 | "store_replica_of_multi_masters_2," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 115 | + str(len(data_list))) 116 | 117 | # 自身が保持しているデータのうち委譲するものを返す. 118 | # 対象となるデータは時計周りに辿った際に 引数 node_id と 自身の node_id 119 | # の間に data_id が位置するデータである. 120 | # join呼び出し時、新たに参加してきた新規ノードに、successorとなる自身が、担当から外れる 121 | # 範囲のデータの委譲を行うために、新規ノードから呼び出される形で用いられる. 122 | # rest_copy引数によってコピーを渡すだけか、完全に委譲してしまい自身のデータストアからは渡したデータを削除 123 | # するかどうか選択できる 124 | def delegate_my_tantou_data(self, node_id : int) -> List[KeyValue]: 125 | with self.existing_node.node_info.lock_of_datastore: 126 | ChordUtil.dprint("delegate_my_tantou_data_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 127 | + ChordUtil.gen_debug_str_of_data(node_id)) 128 | ret_datas : List[KeyValue] = [] 129 | tantou_data: List[DataIdAndValue] = self.get_all_tantou_data(node_id) 130 | 131 | for entry in tantou_data: 132 | # Chordネットワークを右回りにたどった時に、データの id (data_id) が呼び出し元の node_id から 133 | # 自身の node_id の間に位置する場合は、そのデータの担当は自身から変わらないため、渡すデータから 134 | # 除外する 135 | if ChordUtil.exist_between_two_nodes_right_mawari(node_id, self.existing_node.node_info.node_id, entry.data_id): 136 | ChordUtil.dprint( 137 | "delegate_my_tantou_data_2," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 138 | + ChordUtil.gen_debug_str_of_data(node_id) + "," + ChordUtil.gen_debug_str_of_data(entry.data_id)) 139 | continue 140 | 141 | # 文字列の参照をそのまま用いてしまうが、文字列はイミュータブルであるため 142 | # 問題ない 143 | item = KeyValue(None, entry.value_data) 144 | item.data_id = entry.data_id 145 | ret_datas.append(item) 146 | 147 | return ret_datas 148 | 149 | # 存在しないKeyが与えられた場合 KeyErrorがraiseされる 150 | def get(self, data_id : int) -> PResult[Optional[DataIdAndValue]]: 151 | with self.existing_node.node_info.lock_of_datastore: 152 | try: 153 | return PResult.Ok(self.stored_data[str(data_id)]) 154 | except KeyError: 155 | return PResult.Err(None, ErrorCode.KeyError_CODE) 156 | 157 | 158 | # 全ての保持しているデータを返す 159 | def get_all_data(self) -> List[DataIdAndValue]: 160 | ChordUtil.dprint("get_all_data_1," + ChordUtil.gen_debug_str_of_node( 161 | self.existing_node.node_info)) 162 | 163 | with self.existing_node.node_info.lock_of_datastore: 164 | ret_data_list: List[DataIdAndValue] = [] 165 | for key, value in self.stored_data.items(): 166 | ret_data_list.append(DataIdAndValue(data_id=int(key), value_data=value.value_data)) 167 | 168 | ChordUtil.dprint("get_all_data_2," + ChordUtil.gen_debug_str_of_node( 169 | self.existing_node.node_info) + "," 170 | + str(len(ret_data_list))) 171 | 172 | return ret_data_list 173 | 174 | # 担当データ全てのレプリカを successor_info_list内のノードに配る 175 | # 必要なロックは呼び出し元でとってある前提 176 | def distribute_replica(self): 177 | ChordUtil.dprint("distribute_replica_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info)) 178 | 179 | tantou_data_list: List[DataIdAndValue] = self.get_all_tantou_data() 180 | 181 | # レプリカを successorList内のノードに渡す(手抜きでputされたもの含めた全てを渡してしまう) 182 | for succ_info in self.existing_node.node_info.successor_info_list: 183 | ret = ChordUtil.get_node_by_address(succ_info.address_str) 184 | if (ret.is_ok): 185 | succ_node : 'ChordNode' = cast('ChordNode', ret.result) 186 | else: # ret.err_code == ErrorCode.InternalControlFlowException_CODE || ret.err_code == ErrorCode.NodeIsDownedException_CODE 187 | # stabilize処理 と put処理 を経ていずれ正常な状態に 188 | # なるため、ここでは何もせずに次のノードに移る 189 | ChordUtil.dprint( 190 | "distribute_replica_2," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 191 | + ChordUtil.gen_debug_str_of_node(succ_info)) 192 | continue 193 | 194 | # 非効率だが、putやstabilize_successorなどの度に担当データを全て渡してしまう 195 | # TODO: putやstabilize_successorが呼び出される担当データ全てのレプリカを渡すのはあまりに非効率なので、担当データのIDリストを渡して 196 | # 持っていないデータのIDのリストを返してもらい、それらのデータのみ渡すようにいずれ修正する 197 | 198 | # TODO: receive_replica call at distribute_replica 199 | succ_node.endpoints.grpc__receive_replica(tantou_data_list) 200 | 201 | ChordUtil.dprint("distribute_replica_3," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 202 | + ChordUtil.gen_debug_str_of_node(succ_info)) 203 | */ 204 | use std::collections::HashMap; 205 | use std::sync::Arc; 206 | use std::cell::RefCell; 207 | use parking_lot::{ReentrantMutex, const_reentrant_mutex}; 208 | 209 | use crate::gval; 210 | use crate::chord_node; 211 | use crate::node_info; 212 | use crate::chord_util; 213 | use crate::stabilizer; 214 | use crate::router; 215 | use crate::taskqueue; 216 | use crate::endpoints; 217 | 218 | pub const DELETED_ENTRY_MARKING_STR : &str = "THIS_KEY_IS_DELETED"; 219 | pub const DATA_STORE_OP_DIRECT_STORE : &str = "DIRECT_STORE"; 220 | pub const DATA_STORE_OP_DIRECT_REMOVE : &str = "DIRECT_REMOVE"; 221 | 222 | type ArRmRs = Arc>>; 223 | 224 | #[derive(Debug, Clone)] 225 | pub struct DataStore { 226 | // pub existing_node : ArRmRs, 227 | // Keyはハッシュを通されたものなので元データの値とは異なる 228 | pub stored_data : HashMap, 229 | } 230 | 231 | impl DataStore { 232 | pub fn new() -> DataStore { 233 | let sd = HashMap::new(); 234 | DataStore {stored_data : sd} 235 | } 236 | } -------------------------------------------------------------------------------- /chord_sim_rust/src/endpoints.rs: -------------------------------------------------------------------------------- 1 | /* 2 | # coding:utf-8 3 | 4 | from .chord_util import ChordUtil, InternalControlFlowException,\ 5 | NodeIsDownedExceptiopn, DataIdAndValue, KeyValue, PResult 6 | 7 | class Endpoints: 8 | 9 | def __init__(self, existing_node : 'ChordNode'): 10 | self.existing_node = existing_node 11 | 12 | def rrpc__global_put(self, data_id : int, value_str : str) -> bool: 13 | return self.existing_node.global_put(data_id, value_str) 14 | 15 | def grpc__put(self, data_id : int, value_str : str) -> bool: 16 | return self.existing_node.put(data_id, value_str) 17 | 18 | def grpc__global_get_recover_prev(self, data_id : int) -> Tuple[str, Optional['ChordNode']]: 19 | return self.existing_node.global_get_recover_prev(data_id) 20 | 21 | def grpc__global_get_recover_succ(self, data_id: int) -> Tuple[str, Optional['ChordNode']]: 22 | return self.existing_node.global_get_recover_succ(data_id) 23 | 24 | def rrpc__global_get(self, data_id : int) -> str: 25 | return self.existing_node.global_get(data_id) 26 | 27 | def grpc__get(self, data_id : int, for_recovery = False) -> str: 28 | return self.existing_node.get(data_id, for_recovery) 29 | 30 | def grpc__global_delete(self, data_id : int) -> bool: 31 | return self.existing_node.global_delete(data_id) 32 | 33 | def grpc__pass_node_info(self) -> 'NodeInfo': 34 | return self.existing_node.pass_node_info() 35 | 36 | def grpc__get_all_tantou_data(self, node_id : Optional[int] = None) -> List[DataIdAndValue]: 37 | return self.existing_node.data_store.get_all_tantou_data(node_id) 38 | 39 | def grpc__receive_replica(self, pass_datas : List[DataIdAndValue]): 40 | return self.existing_node.data_store.receive_replica(pass_datas) 41 | 42 | def grpc__delegate_my_tantou_data(self, node_id : int) -> List[KeyValue]: 43 | return self.existing_node.data_store.delegate_my_tantou_data(node_id) 44 | 45 | def grpc__get_all_data(self) -> List[DataIdAndValue]: 46 | return self.existing_node.data_store.get_all_data() 47 | 48 | # TODO: AppropriateExp, DownedExp, InternalExp at grpc__find_successor 49 | def grpc__find_successor(self, id : int) -> PResult[Optional['ChordNode']]: 50 | return self.existing_node.router.find_successor(id) 51 | 52 | def grpc__closest_preceding_finger(self, id : int) -> 'ChordNode': 53 | return self.existing_node.router.closest_preceding_finger(id) 54 | 55 | def grpc__pass_successor_list(self) -> List['NodeInfo']: 56 | return self.existing_node.stabilizer.pass_successor_list() 57 | 58 | def grpc__pass_predecessor_info(self) -> Optional['NodeInfo']: 59 | return self.existing_node.stabilizer.pass_predecessor_info() 60 | 61 | def grpc__set_routing_infos_force(self, predecessor_info : 'NodeInfo', successor_info_0 : 'NodeInfo', ftable_enry_0 : 'NodeInfo'): 62 | return self.existing_node.stabilizer.set_routing_infos_force(predecessor_info, successor_info_0, ftable_enry_0) 63 | 64 | # TODO: InternalExp, DownedExp at grpc__stabilize_succesor_inner 65 | def grpc__stabilize_successor_inner(self) -> PResult[Optional['NodeInfo']]: 66 | return self.existing_node.stabilizer.stabilize_successor_inner() 67 | 68 | # TODO: InternalExp at grpc__check_predecessor 69 | def grpc__check_predecessor(self, node_info : 'NodeInfo') -> PResult[bool]: 70 | return self.existing_node.stabilizer.check_predecessor(node_info) 71 | 72 | # TODO: InternalExp at grpc__check_successor_list_length 73 | def grpc__check_successor_list_length(self) -> PResult[bool]: 74 | return self.existing_node.stabilizer.check_successor_list_length() 75 | 76 | # TODO: 実システムでは、ChordNodeオブジェクトが生成されたあとはこのrpcでチェック可能とする 77 | # ただし初回の呼び出しはget_node_by_addressの中で行われ、そこでのチェックを通った場合のみ 78 | # 同メソッドは ChordNodeオブジェクトを返す設計とする(通信回数が増えてしまうがそこは許容する) 79 | def grpc__is_alive(self) -> bool: 80 | raise Exception("not implemented yet") 81 | 82 | # TODO: 実システムでだけ用いる。ノード情報を取得するAPI 83 | # get_nobe_by_address内でgrpc__is_aliveでの生存チェックを通ったら 84 | # このメソッドで暫定的に生成したChordNodeオブジェクトを構築するための情報 85 | # を取得する. 内容としては NodeInfoオブジェクトのうち、successor_info_list 86 | # のみ空リストとなっているものになる見込み 87 | def grpc__get_chord_node_info(self) -> 'NodeInfo': 88 | ret_info : NodeInfo = self.existing_node.node_info.get_partial_deepcopy() 89 | if self.existing_node.node_info.predecessor_info != None: 90 | ret_info.predecessor_info = cast('NodeInfo', self.existing_node.node_info.predecessor_info).get_partial_deepcopy() 91 | return ret_info 92 | */ 93 | use std::sync::Arc; 94 | use std::cell::{RefCell, Ref, RefMut}; 95 | use parking_lot::{ReentrantMutex, const_reentrant_mutex}; 96 | 97 | use crate::gval; 98 | use crate::chord_node; 99 | use crate::node_info; 100 | use crate::chord_util; 101 | use crate::data_store; 102 | use crate::router; 103 | use crate::stabilizer; 104 | use crate::taskqueue; 105 | 106 | type ArRmRs = Arc>>; 107 | 108 | /* 109 | #[derive(Debug, Clone)] 110 | pub struct Endpoints { 111 | // pub existing_node : ArRmRs, 112 | } 113 | 114 | impl Endpoints { 115 | pub fn new() -> Endpoints { 116 | Endpoints {} 117 | } 118 | } 119 | */ 120 | 121 | //TODO: (rust) ダミー実装なので委譲処理が必要になったタイミングで対応すること 122 | pub fn grpc__delegate_my_tantou_data(predecessor: ArRmRs, node_id : u32) -> Vec{ 123 | return vec![]; 124 | //return self.existing_node.data_store.delegate_my_tantou_data(node_id) 125 | } 126 | /* 127 | def grpc__delegate_my_tantou_data(self, node_id : int) -> List[KeyValue]: 128 | return self.existing_node.data_store.delegate_my_tantou_data(node_id) 129 | */ 130 | 131 | pub fn grpc__pass_successor_list(self_node: ArRmRs) -> Vec { 132 | return stabilizer::pass_successor_list(self_node); 133 | } 134 | /* 135 | def grpc__pass_successor_list(self) -> List['NodeInfo']: 136 | return self.existing_node.stabilizer.pass_successor_list() 137 | */ 138 | 139 | pub fn grpc__check_predecessor(self_node: ArRmRs, caller_node_ni: node_info::NodeInfo) -> Result { 140 | return stabilizer::check_predecessor(self_node, caller_node_ni); 141 | } 142 | /* 143 | # TODO: InternalExp at grpc__check_predecessor 144 | def grpc__check_predecessor(self, node_info : 'NodeInfo') -> PResult[bool]: 145 | return self.existing_node.stabilizer.check_predecessor(node_info) 146 | */ 147 | 148 | pub fn grpc__set_routing_infos_force(self_node: ArRmRs, predecessor_info: node_info::NodeInfo, successor_info_0: node_info::NodeInfo , ftable_enry_0: node_info::NodeInfo){ 149 | return stabilizer::set_routing_infos_force(Arc::clone(&self_node), predecessor_info, successor_info_0, ftable_enry_0); 150 | } 151 | /* 152 | def grpc__set_routing_infos_force(self, predecessor_info : 'NodeInfo', successor_info_0 : 'NodeInfo', ftable_enry_0 : 'NodeInfo'): 153 | return self.existing_node.stabilizer.set_routing_infos_force(predecessor_info, successor_info_0, ftable_enry_0) 154 | */ 155 | 156 | // id(int)で識別されるデータを担当するノードの名前解決を行う 157 | // Attention: 適切な担当ノードを得ることができなかった場合、FindNodeFailedExceptionがraiseされる 158 | // TODO: AppropriateExp, DownedExp, InternalExp at find_successor 159 | pub fn grpc__find_successor(existing_node: ArRmRs, exnode_ref: &Ref, exnode_ni_ref: &Ref, id : u32) -> Result, chord_util::GeneralError> { 160 | return router::find_successor(existing_node, exnode_ref, exnode_ni_ref, id); 161 | } 162 | 163 | pub fn grpc__closest_preceding_finger(existing_node: ArRmRs, exnode_ref: &Ref, exnode_ni_ref: &Ref, id : u32) -> ArRmRs { 164 | return router::closest_preceding_finger(existing_node, exnode_ni_ref, id); 165 | } 166 | 167 | pub fn grpc__stabilize_successor_inner(self_node: ArRmRs) -> Result, chord_util::GeneralError>{ 168 | return stabilizer::stabilize_successor_inner(self_node); 169 | } -------------------------------------------------------------------------------- /chord_sim_rust/src/gval.rs: -------------------------------------------------------------------------------- 1 | /* 2 | # coding:utf-8 3 | 4 | import threading 5 | 6 | ID_SPACE_BITS = 30 # 160 <- sha1での本来の値 7 | ID_SPACE_RANGE = 2**ID_SPACE_BITS # 0を含めての数である点に注意 8 | 9 | # paramaters for executing by PyPy3 on my desktop machine 10 | JOIN_INTERVAL_SEC = 1.0 #2.0 #0.9 #0.7 # 0.5 # 1 11 | PUT_INTERVAL_SEC = 0.05 #0.5 # 0.01 #0.5 # 1 12 | GET_INTERVAL_SEC = 0.05 #0.5 # 0.01 #0.5 # 1 13 | 14 | # ノード増加の勢いは 係数-1/係数 となる 15 | NODE_KILL_INTERVAL_SEC = 120.0 #20 #JOIN_INTERVAL_SEC * 10 16 | 17 | # 全ノードがstabilize_successorを実行することを1バッチとした際に 18 | # stabilize処理担当のスレッドにより呼び出されるstabilize処理を行わせる 19 | # メソッドの一回の呼び出しで何バッチが実行されるか 20 | STABILIZE_SUCCESSOR_BATCH_TIMES = 20 #10 #20 21 | # 全ノードがstabilize_finger_tableを複数回呼びされることで、finger_tableの全要素を更新 22 | # することを1バッチとした際に、stabilize処理担当のスレッドにより呼び出されるstabilize処理 23 | # を行わせるメソッドの一回の呼び出しで何バッチが実行されるか 24 | STABILIZE_FTABLE_BATCH_TIMES = 2 #1 25 | 26 | # 一時的にこれより短くなる場合もある 27 | SUCCESSOR_LIST_NORMAL_LEN = 3 28 | 29 | # # 160bit符号なし整数の最大値 30 | # # Chordネットワーク上のID空間の上限 31 | # ID_MAX = 2**ID_SPACE_BITS - 1 32 | 33 | # 30bit符号なし整数の最大値 34 | # Chordネットワーク上のID空間の上限 35 | # TODO: 検証時の実行時間短縮のためにハッシュ関数で求めた値の代わりに乱数 36 | # を用いているため bit数 を少なくしている 37 | ID_MAX = ID_SPACE_RANGE - 1 38 | 39 | KEEP_NODE_NUM = 50 #100 40 | NODE_NUM_MAX = 10000 41 | 42 | LOCK_ACQUIRE_TIMEOUT = 3 #10 43 | 44 | # プロセス内の全てのデータへのアクセスに対するロック変数 45 | # 実装していく過程で細粒度のロックに対応できていない場合や、デバッグ用途に用いる 46 | lock_of_all_data = threading.Lock() 47 | 48 | # TODO: all_node_dictとall_data_listのロックはRustの該当するコレクションがスレッドセーフか 49 | # 確認してから必要なところだけに絞る必要あり(例えば、readアクセスでも結果にセンシティブなところ以外は不要ではないかなど) 50 | 51 | # アドレス文字列をキーとしてとり、対応するノードのChordNodeオブジェクトを返すハッシュ 52 | # IPアドレスが分かれば、対応するノードと通信できることと対応している 53 | all_node_dict : Dict[str, 'ChordNode'] = {} 54 | lock_of_all_node_dict = threading.Lock() 55 | 56 | # DHT上で保持されている全てのデータが保持されているリスト 57 | # KeyValueオブジェクトを要素として持つ 58 | # 全てのノードはputの際はDHTにデータをputするのとは別にこのリストにデータを追加し、 59 | # getする際はDHTに対してgetを発行するためのデータをこのリストからランダム 60 | # に選び、そのkeyを用いて探索を行う. また value も保持しておき、取得できた内容と 61 | # 照らし合わせられるようにする 62 | all_data_list : List['KeyValue'] = [] 63 | lock_of_all_data_list = threading.Lock() 64 | 65 | # 検証を分かりやすくするために何ノード目として生成されたか 66 | # のデバッグ用IDを持たせるためのカウンタ 67 | already_born_node_num = 0 68 | 69 | is_network_constructed = False 70 | 71 | # デバッグ用の変数群 72 | global_get_retry_cnt = 0 73 | GLOBAL_GET_RETRY_CNT_LIMIT_TO_DEBEUG_PRINT = 30 74 | 75 | # マスターデータとレプリカの区別なく、データIDをKeyに、当該IDに対応するデータを 76 | # 保持しているノードのリストを得られる dict 77 | all_data_placement_dict : Dict[str, List['NodeInfo']] = {} 78 | 79 | # 既に発行したputの回数 80 | already_issued_put_cnt = 0 81 | 82 | # stabilize_successorのループの回せる回数の上限 83 | TRYING_GET_SUCC_TIMES_LIMIT = SUCCESSOR_LIST_NORMAL_LEN * 5 84 | 85 | # # デバッグ用のフラグ 86 | # # killスレッドがWriter、他のスレッドはReaderとしてロックを取得する 87 | # kill_thread_lock_factory = rwlock.RWLockFairD() 88 | # kill_thread_write_lock = kill_thread_lock_factory.gen_wlock() 89 | # kill_thread_read_lock = kill_thread_lock_factory.gen_rlock() 90 | 91 | STABILIZE_THREAD_NUM = 3 #10 92 | 93 | ENABLE_DATA_STORE_OPERATION_DPRINT = False 94 | ENABLE_ROUTING_INFO_DPRINT = False 95 | 96 | # partial_join_opが実行されることを待っているノードが存在するか否か 97 | # join と partial_join_op の間で、該当ノードがkillされることを避けるために用いる 98 | is_waiting_partial_join_op_exists = False 99 | */ 100 | 101 | use std::collections::HashMap; 102 | use std::sync::atomic::{AtomicIsize, AtomicBool}; 103 | use std::sync::Arc; 104 | use std::cell::RefCell; 105 | use parking_lot::{ReentrantMutex, const_reentrant_mutex}; 106 | 107 | use crate::chord_node; 108 | use crate::node_info; 109 | use crate::stabilizer; 110 | use crate::router; 111 | use crate::taskqueue; 112 | use crate::endpoints; 113 | use crate::data_store; 114 | use crate::chord_util; 115 | 116 | type ArRmRs = Arc>>; 117 | 118 | pub const ID_SPACE_BITS : u32 = 30; // 160 <- sha1での本来の値 119 | pub const ID_SPACE_RANGE : u32 = 2i32.pow(ID_SPACE_BITS) as u32; // 0を含めての数である点に注意 120 | 121 | // paramaters for executing by PyPy3 on my desktop machine 122 | //pub const JOIN_INTERVAL_SEC : f32 = 1.0; //2.0 //0.9 //0.7 //0.5 //1 123 | pub static mut JOIN_INTERVAL_SEC : AtomicIsize = AtomicIsize::new(1); 124 | pub const PUT_INTERVAL_SEC : f32 = 0.05; //0.5 //0.01 //0.5 # 1 125 | pub const GET_INTERVAL_SEC : f32 = 0.05; //0.5 //0.01 //0.5 //1 126 | 127 | // ノード増加の勢いは 係数-1/係数 となる 128 | pub const NODE_KILL_INTERVAL_SEC : f32 = 120.0; //20 #JOIN_INTERVAL_SEC * 10 129 | 130 | // 全ノードがstabilize_successorを実行することを1バッチとした際に 131 | // stabilize処理担当のスレッドにより呼び出されるstabilize処理を行わせる 132 | // メソッドの一回の呼び出しで何バッチが実行されるか 133 | pub const STABILIZE_SUCCESSOR_BATCH_TIMES : u32 = 20; //10 //20 134 | 135 | // 全ノードがstabilize_finger_tableを複数回呼びされることで、finger_tableの全要素を更新 136 | // することを1バッチとした際に、stabilize処理担当のスレッドにより呼び出されるstabilize処理 137 | // を行わせるメソッドの一回の呼び出しで何バッチが実行されるか 138 | pub const STABILIZE_FTABLE_BATCH_TIMES : i32 = 2; //1 139 | 140 | // 一時的にこれより短くなる場合もある 141 | pub const SUCCESSOR_LIST_NORMAL_LEN : i32 = 3; 142 | 143 | // 160bit符号なし整数の最大値 144 | // Chordネットワーク上のID空間の上限 145 | // ID_MAX = 2**ID_SPACE_BITS - 1 146 | 147 | // 30bit符号なし整数の最大値 148 | // Chordネットワーク上のID空間の上限 149 | // TODO: 検証時の実行時間短縮のためにハッシュ関数で求めた値の代わりに乱数 150 | // を用いているため bit数 を少なくしている 151 | pub const ID_MAX : u32 = ID_SPACE_RANGE - 1; 152 | pub const KEEP_NODE_NUM : i32 = 50; //100 153 | pub const NODE_NUM_MAX : i32 = 50;//10000; 154 | pub const LOCK_ACQUIRE_TIMEOUT : i32 = 3; //10 155 | 156 | // stabilize_successorのループの回せる回数の上限 157 | pub const TRYING_GET_SUCC_TIMES_LIMIT : i32 = SUCCESSOR_LIST_NORMAL_LEN * 5; 158 | pub const STABILIZE_THREAD_NUM : i32 = 1; //3 //10 159 | pub const ENABLE_DATA_STORE_OPERATION_DPRINT : bool = false; 160 | pub const ENABLE_ROUTING_INFO_DPRINT : bool = true; 161 | pub const GLOBAL_GET_RETRY_CNT_LIMIT_TO_DEBEUG_PRINT : i32 = 30; 162 | 163 | // 検証を分かりやすくするために何ノード目として生成されたか 164 | // のデバッグ用IDを持たせるためのカウンタ 165 | pub static mut already_born_node_num : AtomicIsize = AtomicIsize::new(0); 166 | 167 | pub static mut is_network_constructed : AtomicBool = AtomicBool::new(false); 168 | 169 | // デバッグ用の変数群 170 | pub static mut global_get_retry_cnt : AtomicIsize = AtomicIsize::new(0); 171 | 172 | // 既に発行したputの回数 173 | pub static mut already_issued_put_cnt : AtomicIsize = AtomicIsize::new(0); 174 | 175 | // partial_join_opが実行されることを待っているノードが存在するか否か 176 | // join と partial_join_op の間で、該当ノードがkillされることを避けるために用いる 177 | pub static mut is_waiting_partial_join_op_exists : AtomicBool = AtomicBool::new(false); 178 | 179 | //TODO: ジェネリクスで指定している型を適切なものに変更する. GlobalDatas at gval 180 | pub struct GlobalDatas { 181 | // アドレス文字列をキーとしてとり、対応するノードのChordNodeオブジェクトを返すハッシュ 182 | // IPアドレスが分かれば、対応するノードと通信できることと対応している 183 | pub all_node_dict : HashMap>, 184 | // DHT上で保持されている全てのデータが保持されているリスト 185 | // KeyValueオブジェクトを要素として持つ 186 | // 全てのノードはputの際はDHTにデータをputするのとは別にこのリストにデータを追加し、 187 | // getする際はDHTに対してgetを発行するためのデータをこのリストからランダム 188 | // に選び、そのkeyを用いて探索を行う. また value も保持しておき、取得できた内容と 189 | // 照らし合わせられるようにする 190 | pub all_data_list : Vec>, 191 | // 以下のxxx_lock_dictは各ノードのaddress_strをキーに ArRmRsをダミーの 192 | // ミューテックスとして保持する 193 | // 各々のdictは各ChordNodeオブジェクトのフィールドに対応しており、あるノード 194 | // の対応するフィールドの値への参照の取得と関係なしに排他を行うために用いる 195 | // Rustのロック機構の設計では、柔軟にクリティカルセクションを設定することが 196 | // 困難であるため導入した 197 | // クリティカルセクションに入りたいスレッドは、まずHashMapに対応するロック用 198 | // オブジェクトが格納されているか確認し、存在すれば既に格納されていたものを取得する. 199 | // 存在しなければ生成し追加する. 200 | // その後、ロック用オブジェクトのロックを獲得し、排他が必要な処理を行う 201 | // アドレス文字列でノードを特定しているため、排他を行う対象のオブジェクトの実体は 202 | // 大本でもコピーされたものでも関係はない 203 | pub ninfo_lock_dict: HashMap>, 204 | pub dstore_lock_dict: HashMap>, 205 | pub tqueue_lock_dict: HashMap> 206 | } 207 | 208 | impl GlobalDatas { 209 | pub fn new() -> GlobalDatas { 210 | GlobalDatas { 211 | all_node_dict : HashMap::new(), 212 | all_data_list : Vec::new(), 213 | ninfo_lock_dict : HashMap::new(), 214 | dstore_lock_dict : HashMap::new(), 215 | tqueue_lock_dict : HashMap::new() 216 | } 217 | } 218 | } 219 | 220 | lazy_static! { 221 | pub static ref global_datas : ArRmRs = ArRmRs_new!(GlobalDatas::new()); 222 | } 223 | 224 | /* 225 | // マスターデータとレプリカの区別なく、データIDをKeyに、当該IDに対応するデータを 226 | // 保持しているノードのリストを得られる dict 227 | all_data_placement_dict : Dict[str, List['NodeInfo']] = {} 228 | */ -------------------------------------------------------------------------------- /chord_sim_rust/src/node_info.rs: -------------------------------------------------------------------------------- 1 | /* 2 | # coding:utf-8 3 | 4 | import copy 5 | 6 | from . import gval 7 | from .chord_util import ChordUtil 8 | import threading 9 | 10 | # メモ: オブジェクトをdictのキーとして使用可能としてある 11 | class NodeInfo: 12 | 13 | def __init__(self): 14 | self.node_id: int = -1 15 | self.address_str: str = "" 16 | 17 | # デバッグ用のID 18 | # 何ノード目として生成されたかの値 19 | # TODO: 実システムでは開発中(というか、スクリプトで順にノード起動していくような形)でないと 20 | # 利用できないことは念頭おいて置く必要あり NodeInfo#born_id 21 | self.born_id: int = -1 22 | 23 | # 以下の2つはNodeInfoオブジェクトを保持. 24 | # ある時点で取得したものが保持されており、変化する場合のあるフィールド 25 | # の内容は最新の内容となっているとは限らないため注意が必要. 26 | # そのような情報が必要な場合はChordNodeオブジェクトから参照し、 27 | # 必要であれば、その際に下のフィールドにdeepcopyを設定しなおさ 28 | # なければならない. 29 | 30 | # 状況に応じて伸縮するが、インデックス0には必ず 非None な要素が入っている 31 | # ように制御する 32 | self.successor_info_list: List[NodeInfo] = [] 33 | # join後はNoneになることのないように制御される 34 | self.predecessor_info: Optional[NodeInfo] = None 35 | 36 | # predecessor_info と successor_info_list のそれぞれに対応する 37 | # ロック変数(re-entrantロック) 38 | self.lock_of_pred_info : threading.RLock = threading.RLock() 39 | self.lock_of_succ_infos : threading.RLock = threading.RLock() 40 | 41 | # stored_data, master2data_idx、master_node_dict 全てのフィールドに対する 42 | # ロック変数(re-entrantロック) 43 | self.lock_of_datastore : threading.RLock = threading.RLock() 44 | 45 | # NodeInfoオブジェクトを要素として持つリスト 46 | # インデックスの小さい方から狭い範囲が格納される形で保持する 47 | # sha1で生成されるハッシュ値は160bit符号無し整数であるため要素数は160となる 48 | # TODO: 現在は ID_SPACE_BITS が検証時の実行時間の短縮のため30となっている 49 | self.finger_table: List[Optional[NodeInfo]] = [None] * gval.ID_SPACE_BITS 50 | 51 | # 単純にdeepcopyするとチェーン構造になっているものが全てコピーされてしまう 52 | # ため、そこの考慮を行い、また、finger_tableはコピーしない形での deepcopy 53 | # を返す. 54 | # 上述の考慮により、コピーした NodeInfoオブジェクト の successor_infoと 55 | # predecessor_infoは deepcopy の対象ではあるが、それらの中の同名のフィールド 56 | # にはNoneが設定される. これにより、あるノードがコピーされた NodeInfo を保持 57 | # した場合、predecessor や successorは辿ることができるが、その先は辿ることが 58 | # 直接的にはできないことになる(predecessor や successorの ChordNodeオブジェクト 59 | # を引いてやれば可能) 60 | # 用途としては、あるノードの node_info を他のノードが取得し保持する際に利用される 61 | # ことを想定して実装されている. 62 | def get_partial_deepcopy(self) -> 'NodeInfo': 63 | ret_node_info: NodeInfo = NodeInfo() 64 | 65 | ret_node_info.node_id = copy.copy(self.node_id) 66 | ret_node_info.address_str = copy.copy(self.address_str) 67 | ret_node_info.born_id = copy.copy(self.born_id) 68 | ret_node_info.successor_info_list = [] 69 | ret_node_info.predecessor_info = None 70 | 71 | # ロック関連のフィールドは本メソッドでコピーすることで生まれた 72 | # オブジェクトにおいて利用されることがあったとしても、ロックの 73 | # 対象は上記でコピーしているオブジェクトではなく、フィールドそのもの 74 | # であるため、コピーの必要はない 75 | 76 | return ret_node_info 77 | 78 | def __eq__(self, other): 79 | if not isinstance(other, NodeInfo): 80 | return False 81 | return self.node_id == other.node_id 82 | 83 | def __hash__(self): 84 | return self.node_id 85 | 86 | def __str__(self): 87 | return ChordUtil.gen_debug_str_of_node(self) 88 | */ 89 | 90 | use std::sync::Arc; 91 | use std::cell::{RefMut, RefCell, Ref}; 92 | use parking_lot::{ReentrantMutex, const_reentrant_mutex}; 93 | 94 | use crate::gval; 95 | use crate::chord_node; 96 | use crate::chord_util; 97 | use crate::stabilizer; 98 | use crate::taskqueue; 99 | use crate::endpoints; 100 | use crate::data_store; 101 | use crate::router; 102 | 103 | type ArRmRs = Arc>>; 104 | 105 | #[derive(Debug)] 106 | pub struct NodeInfo { 107 | // pub existing_node : ArRmRs, 108 | pub node_id : u32, 109 | pub address_str: String, 110 | // デバッグ用のID 111 | // 何ノード目として生成されたかの値 112 | // TODO: 実システムでは開発中(というか、スクリプトで順にノード起動していくような形)でないと 113 | // 利用できないことは念頭おいて置く必要あり NodeInfo#born_id 114 | pub born_id : i32, 115 | // 以下の2つはNodeInfoオブジェクトを保持. 116 | // ある時点で取得したものが保持されており、変化する場合のあるフィールド 117 | // の内容は最新の内容となっているとは限らないため注意が必要. 118 | // そのような情報が必要な場合はChordNodeオブジェクトから参照し、 119 | // 必要であれば、その際に下のフィールドにdeepcopyを設定しなおさ 120 | // なければならない. 121 | // 122 | // 状況に応じて伸縮するが、インデックス0には必ず 非None な要素が入っている 123 | // ように制御する 124 | pub successor_info_list: Vec, 125 | // join後はNoneになることのないように制御される 126 | // Optionだと再帰的定義となってコンパイルエラーとなり、 127 | // Arc> とすると参照アクセスする時にうまくいかないので 128 | // 要素数が0もしくは1のVecとして定義する。Noneに対応する状態はlen()の結果が0の時 129 | // 格納されている要素自体はimmutableとして扱わなければならないので注意 130 | pub predecessor_info: Vec, 131 | // NodeInfoオブジェクトを要素として持つリスト 132 | // インデックスの小さい方から狭い範囲が格納される形で保持する 133 | // sha1で生成されるハッシュ値は160bit符号無し整数であるため要素数は160となる 134 | // TODO: 現在は ID_SPACE_BITS が検証時の実行時間の短縮のため30となっている 135 | 136 | pub finger_table: Vec>, // = [None] * gval.ID_SPACE_BITS 137 | } 138 | 139 | impl NodeInfo { 140 | pub fn new() -> NodeInfo { 141 | NodeInfo { 142 | node_id : 0, //TODO: node_idの初期値を-1から0に変更したので注意 143 | address_str: "".to_string(), 144 | born_id : -1, 145 | successor_info_list : Vec::new(), 146 | predecessor_info : Vec::new(), 147 | finger_table : vec![None; gval::ID_SPACE_BITS as usize] 148 | } 149 | } 150 | 151 | pub fn set_pred_info(&mut self, node_info: NodeInfo){ 152 | if self.predecessor_info.len() == 0 { 153 | self.predecessor_info.push(node_info); 154 | }else{ 155 | self.predecessor_info[0] = node_info; 156 | } 157 | } 158 | } 159 | 160 | // 単純にdeepcopyするとチェーン構造になっているものが全てコピーされてしまう 161 | // ため、そこの考慮を行い、また、finger_tableはコピーしない形での deepcopy 162 | // を返す. 163 | // 上述の考慮により、コピーした NodeInfoオブジェクト の successor_infoと 164 | // predecessor_infoは deepcopy の対象ではあるが、それらの中の同名のフィールド 165 | // にはNoneが設定される. これにより、あるノードがコピーされた NodeInfo を保持 166 | // した場合、predecessor や successorは辿ることができるが、その先は辿ることが 167 | // 直接的にはできないことになる(predecessor や successorの ChordNodeオブジェクト 168 | // を引いてやれば可能) 169 | // 用途としては、あるノードの node_info を他のノードが取得し保持する際に利用される 170 | // ことを想定して実装されている. 171 | impl Clone for NodeInfo { 172 | fn clone(&self) -> Self { 173 | let mut ret_node_info = NodeInfo::new(); 174 | 175 | ret_node_info.node_id = self.node_id; 176 | ret_node_info.address_str = self.address_str.clone(); 177 | ret_node_info.born_id = self.born_id; 178 | ret_node_info.successor_info_list = vec![]; 179 | ret_node_info.predecessor_info = vec![]; 180 | 181 | return ret_node_info; 182 | } 183 | } 184 | 185 | // 上のClone trait 実装の参照から作れる版 186 | pub fn partial_clone_from_ref(node_info_ref: &NodeInfo) -> NodeInfo { 187 | let mut ret_node_info = NodeInfo::new(); 188 | 189 | ret_node_info.node_id = node_info_ref.node_id; 190 | ret_node_info.address_str = node_info_ref.address_str.clone(); 191 | ret_node_info.born_id = node_info_ref.born_id; 192 | ret_node_info.successor_info_list = vec![]; 193 | ret_node_info.predecessor_info = vec![]; 194 | 195 | return ret_node_info; 196 | } 197 | 198 | /* 199 | pub fn get_partial_deepcopy(orig_node_info: &Ref) -> NodeInfo { 200 | let mut ret_node_info = NodeInfo::new(); 201 | 202 | ret_node_info.node_id = orig_node_info.node_id; 203 | ret_node_info.address_str = orig_node_info.address_str.clone(); 204 | ret_node_info.born_id = orig_node_info.born_id; 205 | ret_node_info.successor_info_list = vec![]; 206 | ret_node_info.predecessor_info = vec![]; 207 | 208 | return ret_node_info; 209 | } 210 | */ 211 | 212 | /* 213 | pub fn get_partial_deepcopy(orig_node_info: &Ref) -> ArRmRs { 214 | let ret_node_info = RefCell::new(NodeInfo::new()); 215 | { 216 | let ret_node_info_refmut = ret_node_info.borrow_mut(); 217 | ret_node_info_refmut.node_id = orig_node_info.node_id; 218 | ret_node_info_refmut.address_str = orig_node_info.address_str; 219 | ret_node_info_refmut.born_id = orig_node_info.born_id; 220 | ret_node_info_refmut.successor_info_list = vec![]; 221 | ret_node_info_refmut.predecessor_info = vec![]; 222 | } 223 | 224 | return Arc::new(const_reentrant_mutex(ret_node_info)); 225 | } 226 | */ 227 | 228 | /* 229 | # 単純にdeepcopyするとチェーン構造になっているものが全てコピーされてしまう 230 | # ため、そこの考慮を行い、また、finger_tableはコピーしない形での deepcopy 231 | # を返す. 232 | # 上述の考慮により、コピーした NodeInfoオブジェクト の successor_infoと 233 | # predecessor_infoは deepcopy の対象ではあるが、それらの中の同名のフィールド 234 | # にはNoneが設定される. これにより、あるノードがコピーされた NodeInfo を保持 235 | # した場合、predecessor や successorは辿ることができるが、その先は辿ることが 236 | # 直接的にはできないことになる(predecessor や successorの ChordNodeオブジェクト 237 | # を引いてやれば可能) 238 | # 用途としては、あるノードの node_info を他のノードが取得し保持する際に利用される 239 | # ことを想定して実装されている. 240 | def get_partial_deepcopy(self) -> 'NodeInfo': 241 | ret_node_info: NodeInfo = NodeInfo() 242 | 243 | ret_node_info.node_id = copy.copy(self.node_id) 244 | ret_node_info.address_str = copy.copy(self.address_str) 245 | ret_node_info.born_id = copy.copy(self.born_id) 246 | ret_node_info.successor_info_list = [] 247 | ret_node_info.predecessor_info = None 248 | 249 | # ロック関連のフィールドは本メソッドでコピーすることで生まれた 250 | # オブジェクトにおいて利用されることがあったとしても、ロックの 251 | # 対象は上記でコピーしているオブジェクトではなく、フィールドそのもの 252 | # であるため、コピーの必要はない 253 | 254 | return ret_node_info 255 | */ -------------------------------------------------------------------------------- /chord_sim_rust/src/taskqueue.rs: -------------------------------------------------------------------------------- 1 | /* 2 | # coding:utf-8 3 | 4 | from .chord_util import ChordUtil, InternalControlFlowException, NodeIsDownedExceptiopn 5 | 6 | class TaskQueue: 7 | JOIN_PARTIAL = "join_partial" 8 | 9 | def __init__(self, existing_node : 'ChordNode'): 10 | self.tqueue : List[str] = [] 11 | self.existing_node = existing_node 12 | 13 | def append_task(self, task_code : str): 14 | self.tqueue.append(task_code) 15 | 16 | # キュー内の最初のタスクを実行する 17 | # 処理が失敗した場合は先頭に戻す 18 | def exec_first(self): 19 | if len(self.tqueue) > 0: 20 | ChordUtil.dprint("exec_first_0," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," + str(self.tqueue)) 21 | task_code : str = self.tqueue.pop() 22 | if task_code == TaskQueue.JOIN_PARTIAL: 23 | ret = self.existing_node.stabilizer.partial_join_op() 24 | if (ret.is_ok): 25 | pass 26 | else: # ret.err_code == ErrorCode.InternalControlFlowException_CODE 27 | # 実行に失敗したため再実行すべく先頭に戻す 28 | self.tqueue.insert(0, task_code) 29 | ChordUtil.dprint( 30 | "exec_first_1," + ChordUtil.gen_debug_str_of_node(self.existing_node.node_info) + "," 31 | + "INTERNAL_CONTROL_FLOW_EXCEPTION_OCCURED") 32 | */ 33 | use std::sync::Arc; 34 | use std::cell::RefCell; 35 | use parking_lot::{ReentrantMutex, const_reentrant_mutex}; 36 | 37 | use crate::gval; 38 | use crate::chord_node; 39 | use crate::node_info; 40 | use crate::chord_util; 41 | use crate::endpoints; 42 | use crate::data_store; 43 | use crate::router; 44 | use crate::stabilizer; 45 | 46 | type ArRmRs = Arc>>; 47 | 48 | pub const JOIN_PARTIAL : &str = "join_partial"; 49 | 50 | #[derive(Debug, Clone)] 51 | pub struct TaskQueue { 52 | // pub existing_node : ArRmRs, 53 | pub tqueue : Vec 54 | } 55 | 56 | impl TaskQueue { 57 | pub fn new() -> TaskQueue { 58 | let tq = Vec::new(); 59 | TaskQueue {tqueue: tq} 60 | } 61 | } -------------------------------------------------------------------------------- /rust_env.txt: -------------------------------------------------------------------------------- 1 | current codebase can be builded and run at least rust env below (windows) 2 | 3 | $ rustc --version 4 | rustc 1.58.0-nightly (0d1754e8b 2021-11-05) 5 | $ cargo --version 6 | cargo 1.58.0-nightly (94ca096af 2021-10-29) 7 | $ rustup --version 8 | rustup 1.24.1 (a01bd6b0d 2021-04-27) 9 | info: This is the version for the rustup toolchain manager, not the rustc compiler. 10 | info: The currently active `rustc` version is `rustc 1.58.0-nightly (0d1754e8b 2021-11-05)` 11 | -------------------------------------------------------------------------------- /src/chord_node.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::{AtomicIsize, AtomicBool}; 2 | use std::sync::{Arc, Mutex}; 3 | use std::cell::{RefMut, RefCell, Ref}; 4 | use std::borrow::Borrow; 5 | use std::sync::atomic::Ordering; 6 | 7 | use crate::gval; 8 | use crate::node_info; 9 | use crate::chord_util; 10 | use crate::stabilizer; 11 | use crate::router; 12 | use crate::data_store; 13 | use crate::endpoints; 14 | 15 | type ArMu = Arc>; 16 | 17 | pub fn global_put(self_node: ArMu, data_store: ArMu, key_str: String, val_str: String) -> Result { 18 | let mut self_node_ref = self_node.lock().unwrap(); 19 | let self_node_deep_cloned = node_info::partial_clone_from_ref_strong(&self_node_ref); 20 | drop(self_node_ref); 21 | 22 | // 更新に失敗するレプリカがあった場合、それはノードダウンであると(本当にそうか確実ではないが)前提をおいて、 23 | // 続くレプリカの更新は継続する 24 | let data_id = chord_util::hash_str_to_int(&key_str); 25 | for idx in 0..(gval::REPLICA_NUM + 1) { 26 | let target_id = chord_util::overflow_check_and_conv(data_id as u64 + (gval::REPLICA_ID_DISTANCE as u64) * (idx as u64)); 27 | let replica_node = match endpoints::rrpc_call__find_successor(&self_node_deep_cloned, target_id){ 28 | Err(err) => { 29 | self_node_ref = self_node.lock().unwrap(); 30 | node_info::handle_downed_node_info(&mut self_node_ref, &self_node_deep_cloned, &err); 31 | drop(self_node_ref); 32 | //return Err(err); 33 | continue; 34 | } 35 | Ok(ninfo) => ninfo 36 | }; 37 | 38 | // chord_util::dprint(&("global_put_1,".to_string() 39 | // + chord_util::gen_debug_str_of_node(&self_node_deep_cloned).as_str() + "," 40 | // + chord_util::gen_debug_str_of_node(&replica_node).as_str() + "," 41 | // + chord_util::gen_debug_str_of_data(data_id).as_str() + "," 42 | // + chord_util::gen_debug_str_of_data(target_id).as_str() + "," 43 | // + idx.to_string().as_str() 44 | // )); 45 | 46 | let is_exist = match endpoints::rrpc_call__put(&node_info::gen_node_info_from_summary(&replica_node), target_id, val_str.clone()){ 47 | Err(err) => { 48 | self_node_ref = self_node.lock().unwrap(); 49 | node_info::handle_downed_node_info(&mut self_node_ref, &node_info::gen_node_info_from_summary(&replica_node), &err); 50 | drop(self_node_ref); 51 | continue; 52 | //return Err(err); 53 | } 54 | Ok(is_exist) => is_exist 55 | }; 56 | 57 | // chord_util::dprint(&("global_put_2,".to_string() 58 | // + chord_util::gen_debug_str_of_node(&self_node_deep_cloned).as_str() + "," 59 | // + chord_util::gen_debug_str_of_node(&replica_node).as_str() + "," 60 | // + chord_util::gen_debug_str_of_data(data_id).as_str() + "," 61 | // + chord_util::gen_debug_str_of_data(target_id).as_str() + "," 62 | // + idx.to_string().as_str() 63 | // )); 64 | } 65 | 66 | return Ok(true); 67 | } 68 | 69 | pub fn put(self_node: ArMu, data_store: ArMu, key_id: u32, val_str: String) -> Result { 70 | let self_node_ref = self_node.lock().unwrap(); 71 | let self_node_deep_cloned = node_info::partial_clone_from_ref_strong(&self_node_ref); 72 | drop(self_node_ref); 73 | 74 | chord_util::dprint( 75 | &("put_1,".to_string() 76 | + chord_util::gen_debug_str_of_node(&self_node_deep_cloned).as_str() + "," 77 | + chord_util::gen_debug_str_of_data(key_id).as_str()) 78 | ); 79 | 80 | // 担当範囲(predecessorのidと自身のidの間)のデータであるかチェックする 81 | // そこに収まっていなかった場合、一定時間後リトライが行われるようエラーを返す 82 | // リクエストを受けるという実装も可能だが、stabilize処理で predecessor が生きて 83 | // いるノードとなるまで下手にデータを持たない方が、データ配置の整合性を壊すリスクが 84 | // 減りそうな気がするので、そうする 85 | if self_node_deep_cloned.predecessor_info.len() == 0 { 86 | return Err(chord_util::GeneralError::new("predecessor is None".to_string(), chord_util::ERR_CODE_PRED_IS_NONE)); 87 | } 88 | 89 | // chord_util::dprint( 90 | // &("put_2,".to_string() 91 | // + chord_util::gen_debug_str_of_node(&self_node_deep_cloned).as_str() + "," 92 | // + chord_util::gen_debug_str_of_data(key_id).as_str() + "," 93 | // + val_str.clone().as_str()) 94 | // ); 95 | 96 | 97 | // Chordネットワークを右回りにたどった時に、データの id (key_id) が predecessor の node_id から 98 | // 自身の node_id の間に位置する場合、そのデータは自身の担当だが、そうではない場合 99 | if chord_util::exist_between_two_nodes_right_mawari( 100 | self_node_deep_cloned.predecessor_info[0].node_id, 101 | self_node_deep_cloned.node_id, 102 | key_id) == false { 103 | return Err(chord_util::GeneralError::new("passed data is out of my tantou range".to_string(), chord_util::ERR_CODE_NOT_TANTOU)); 104 | } 105 | 106 | 107 | // chord_util::dprint( 108 | // &("put_3,".to_string() 109 | // + chord_util::gen_debug_str_of_node(&self_node_deep_cloned).as_str() + "," 110 | // + chord_util::gen_debug_str_of_data(key_id).as_str() + "," 111 | // + val_str.clone().as_str()) 112 | // ); 113 | 114 | let mut data_store_ref = data_store.lock().unwrap(); 115 | let ret = data_store_ref.store_one_iv(key_id, val_str); 116 | drop(data_store_ref); 117 | 118 | // chord_util::dprint( 119 | // &("put_4,".to_string() 120 | // + chord_util::gen_debug_str_of_node(&self_node_deep_cloned).as_str() + "," 121 | // + chord_util::gen_debug_str_of_data(key_id).as_str() + "," 122 | // + val_str.clone().as_str() + "," 123 | // + ret.to_string().as_str()) 124 | // ); 125 | 126 | return Ok(ret); 127 | } 128 | 129 | // 得られた value の文字列を返す 130 | // データの取得に失敗した場合は ERR_CODE_QUERIED_DATA_NOT_FOUND をエラーとして返す 131 | // 取得対象のデータが削除済みのデータであった場合は DELETED_ENTRY_MARKING_STR が正常値として返る 132 | pub fn global_get(self_node: ArMu, data_store: ArMu, key_str: String) -> Result { 133 | 134 | let mut self_node_ref = self_node.lock().unwrap(); 135 | let self_node_deep_cloned = node_info::partial_clone_from_ref_strong(&self_node_ref); 136 | drop(self_node_ref); 137 | 138 | let data_id = chord_util::hash_str_to_int(&key_str); 139 | for idx in 0..(gval::REPLICA_NUM + 1) { 140 | let target_id = chord_util::overflow_check_and_conv(data_id as u64 + (gval::REPLICA_ID_DISTANCE as u64) * (idx as u64)); 141 | let replica_node = match endpoints::rrpc_call__find_successor(&self_node_deep_cloned, target_id){ 142 | Err(err) => { 143 | self_node_ref = self_node.lock().unwrap(); 144 | node_info::handle_downed_node_info(&mut self_node_ref, &self_node_deep_cloned, &err); 145 | drop(self_node_ref); 146 | continue; 147 | //return Err(err); 148 | } 149 | Ok(ninfo) => ninfo 150 | }; 151 | 152 | // chord_util::dprint(&("global_get_1,".to_string() 153 | // + chord_util::gen_debug_str_of_node(&self_node_deep_cloned).as_str() + "," 154 | // + chord_util::gen_debug_str_of_node(&replica_node).as_str() + "," 155 | // + chord_util::gen_debug_str_of_data(data_id).as_str() + "," 156 | // + chord_util::gen_debug_str_of_data(target_id).as_str() + "," 157 | // + idx.to_string().as_str() 158 | // )); 159 | /* 160 | if (ret.is_ok): 161 | target_node: 'ChordNode' = cast('ChordNode', ret.result) 162 | # リトライは不要であったため、リトライ用情報の存在を判定するフィールドを 163 | # 初期化しておく 164 | ChordNode.need_put_retry_data_id = -1 165 | else: # ret.err_code == ErrorCode.AppropriateNodeNotFoundException_CODE || ret.err_code == ErrorCode.InternalControlFlowException_CODE || ret.err_code == ErrorCode.NodeIsDownedException_CODE 166 | # 適切なノードを得られなかった、もしくは join処理中のノードを扱おうとしてしまい例外発生 167 | # となってしまったため次回呼び出し時にリトライする形で呼び出しをうけられるように情報を設定しておく 168 | ChordNode.need_put_retry_data_id = data_id 169 | ChordNode.need_put_retry_node = self 170 | ChordUtil.dprint("global_put_1,RETRY_IS_NEEDED" + ChordUtil.gen_debug_str_of_node(self.node_info) + "," 171 | + ChordUtil.gen_debug_str_of_data(data_id)) 172 | return False 173 | */ 174 | 175 | let data_iv = match endpoints::rrpc_call__get(&node_info::gen_node_info_from_summary(&replica_node), target_id){ 176 | Err(err) => { 177 | self_node_ref = self_node.lock().unwrap(); 178 | node_info::handle_downed_node_info(&mut self_node_ref, &node_info::gen_node_info_from_summary(&replica_node), &err); 179 | drop(self_node_ref); 180 | continue; 181 | //return Err(err); 182 | } 183 | Ok(data_iv) => { 184 | // chord_util::dprint(&("global_get_2,".to_string() 185 | // + chord_util::gen_debug_str_of_node(&self_node_deep_cloned).as_str() + "," 186 | // + chord_util::gen_debug_str_of_node(&replica_node).as_str() + "," 187 | // + chord_util::gen_debug_str_of_data(data_id).as_str() + "," 188 | // + chord_util::gen_debug_str_of_data(target_id).as_str() + "," 189 | // + idx.to_string().as_str())); 190 | return Ok(data_iv); 191 | } 192 | }; 193 | } 194 | 195 | return Err(chord_util::GeneralError::new("QUERIED DATA NOT FOUND".to_string(), chord_util::ERR_CODE_QUERIED_DATA_NOT_FOUND)); 196 | } 197 | 198 | pub fn get(self_node: ArMu, data_store: ArMu, key_id: u32) -> Result { 199 | let self_node_ref = self_node.lock().unwrap(); 200 | let self_node_deep_cloned = node_info::partial_clone_from_ref_strong(&self_node_ref); 201 | drop(self_node_ref); 202 | 203 | chord_util::dprint( 204 | &("get_1,".to_string() 205 | + chord_util::gen_debug_str_of_node(&self_node_deep_cloned).as_str() + "," 206 | + chord_util::gen_debug_str_of_data(key_id).as_str()) 207 | ); 208 | 209 | // 担当範囲(predecessorのidと自身のidの間)のデータであるかチェックする 210 | // そこに収まっていなかった場合、一定時間後リトライが行われるようエラーを返す 211 | // リクエストを受けるという実装も可能だが、stabilize処理で predecessor が生きて 212 | // いるノードとなるまで下手にデータを持たない方が、データ配置の整合性を壊すリスクが 213 | // 減りそうな気がするので、そうする 214 | if self_node_deep_cloned.predecessor_info.len() == 0 { 215 | return Err(chord_util::GeneralError::new("predecessor is None".to_string(), chord_util::ERR_CODE_PRED_IS_NONE)); 216 | } 217 | 218 | chord_util::dprint( 219 | &("get_2,".to_string() 220 | + chord_util::gen_debug_str_of_node(&self_node_deep_cloned).as_str() + "," 221 | + chord_util::gen_debug_str_of_data(key_id).as_str()) 222 | ); 223 | 224 | /* 225 | // Chordネットワークを右回りにたどった時に、データの id (key_id) が predecessor の node_id から 226 | // 自身の node_id の間に位置する場合、そのデータは自身の担当だが、そうではない場合 227 | if chord_util::exist_between_two_nodes_right_mawari( 228 | self_node_deep_cloned.predecessor_info[0].node_id, 229 | self_node_deep_cloned.node_id, 230 | key_id) == false { 231 | return Err(chord_util::GeneralError::new("passed data is out of my tantou range".to_string(), chord_util::ERR_CODE_NOT_TANTOU)); 232 | } 233 | */ 234 | 235 | chord_util::dprint( 236 | &("get_3,".to_string() 237 | + chord_util::gen_debug_str_of_node(&self_node_deep_cloned).as_str() + "," 238 | + chord_util::gen_debug_str_of_data(key_id).as_str()) 239 | ); 240 | 241 | let data_store_ref = data_store.lock().unwrap(); 242 | let ret_val = match data_store_ref.get(key_id){ 243 | Err(err) => { 244 | return Err(err); 245 | } 246 | Ok(data_iv) => { 247 | if data_iv.val_str == data_store::DELETED_ENTRY_MARKING_STR.to_string() { 248 | return Err(chord_util::GeneralError::new(data_store::DELETED_ENTRY_MARKING_STR.to_string(), chord_util::ERR_CODE_DATA_TO_GET_IS_DELETED)); 249 | } 250 | data_iv 251 | } 252 | }; 253 | 254 | drop(data_store_ref); 255 | 256 | // chord_util::dprint( 257 | // &("get_4,".to_string() 258 | // + chord_util::gen_debug_str_of_node(&self_node_deep_cloned).as_str() + "," 259 | // + chord_util::gen_debug_str_of_data(key_id).as_str() + "," 260 | // + ret_val.val_str.clone().as_str()) 261 | // ); 262 | 263 | return Ok(ret_val); 264 | } 265 | 266 | pub fn global_delete(self_node: ArMu, data_store: ArMu, key_str: String) -> Result { 267 | match global_get(Arc::clone(&self_node), Arc::clone(&data_store), key_str.clone()){ 268 | Err(err) => { return Err(err); } 269 | Ok(data_iv) => { 270 | match global_put(Arc::clone(&self_node), Arc::clone(&data_store), key_str, data_store::DELETED_ENTRY_MARKING_STR.to_string()){ 271 | Err(err) => { return Err(err); } 272 | Ok(is_exist) => { 273 | return Ok(is_exist); 274 | } 275 | } 276 | } 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /src/chord_util.rs: -------------------------------------------------------------------------------- 1 | extern crate rand; 2 | 3 | use std::sync::{Arc, Mutex}; 4 | use std::borrow::{Borrow, BorrowMut}; 5 | use std::cell::RefMut; 6 | use std::cell::RefCell; 7 | use std::sync::atomic::Ordering; 8 | use std::time::{SystemTime, UNIX_EPOCH}; 9 | use std::collections::hash_map::DefaultHasher; 10 | use std::hash::Hasher; 11 | 12 | use rand::Rng; 13 | use chrono::{Local, DateTime, Date}; 14 | use serde::{Serialize, Deserialize}; 15 | 16 | use crate::gval; 17 | use crate::chord_node; 18 | use crate::node_info; 19 | use crate::stabilizer; 20 | use crate::router; 21 | use crate::endpoints; 22 | use crate::data_store; 23 | 24 | type ArMu = Arc>; 25 | 26 | // TODO: (rustr)ディープコピーを取得するメソッドを定義しておきたい at DataIdAndValue 27 | #[derive(Serialize, Deserialize)] 28 | #[derive(Debug, Clone)] 29 | pub struct DataIdAndValue { 30 | pub data_id : u32, 31 | pub val_str : String 32 | } 33 | 34 | impl DataIdAndValue { 35 | pub fn new(data_id : u32, val_str : String) -> DataIdAndValue { 36 | DataIdAndValue {data_id : data_id, val_str : val_str} 37 | } 38 | } 39 | 40 | // GeneralError型で利用するエラーコード 41 | pub const ERR_CODE_NOT_IMPLEMENTED : u32 = 0; 42 | pub const ERR_CODE_NODE_IS_DOWNED : u32 = 1; 43 | pub const ERR_CODE_APPROPRIATE_NODE_NOT_FOND : u32 = 2; 44 | pub const ERR_CODE_INTERNAL_CONTROL_FLOW_PROBLEM : u32 = 3; 45 | pub const ERR_CODE_HTTP_REQUEST_ERR : u32 = 4; 46 | pub const ERR_CODE_PRED_IS_NONE: u32 = 5; 47 | pub const ERR_CODE_NOT_TANTOU: u32 = 6; 48 | pub const ERR_CODE_QUERIED_DATA_NOT_FOUND: u32 = 7; 49 | pub const ERR_CODE_DATA_TO_GET_NOT_FOUND: u32 = 8; 50 | pub const ERR_CODE_DATA_TO_GET_IS_DELETED: u32 = 9; 51 | 52 | #[derive(Serialize, Deserialize)] 53 | #[derive(Debug, Clone)] 54 | pub struct GeneralError { 55 | pub message: String, 56 | pub line : usize, 57 | pub column: usize, 58 | pub err_code: u32, 59 | } 60 | 61 | impl GeneralError { 62 | pub fn new(message: String, err_code: u32) -> GeneralError { 63 | GeneralError {message: message, line: 0, column: 0, err_code: err_code} 64 | } 65 | } 66 | 67 | impl std::fmt::Display for GeneralError { 68 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 69 | return write!(f, "({})", self.message); 70 | } 71 | } 72 | 73 | // 0からlimitより1少ない数までの値の乱数を返す 74 | pub fn get_rnd_int_with_limit(limit : u32) -> u32{ 75 | let mut rng = rand::thread_rng(); // 乱数生成器の初期化 76 | let limit_inner:i32 = limit as i32; 77 | let rand_val: i32 = rng.gen_range(0..limit_inner); 78 | return rand_val as u32; 79 | } 80 | 81 | // 任意の文字列をハッシュ値(定められたbit数で表現される整数値)に変換しint型で返す 82 | // RustのDefaultHasherでハッシュをとった64bit値の下位32bitを u32 型で返す 83 | pub fn hash_str_to_int(input_str : &String) -> u32 { 84 | let mut hasher = DefaultHasher::new(); 85 | let str_bytes = input_str.as_bytes(); 86 | 87 | for elem in str_bytes{ 88 | hasher.write_u8(*elem); 89 | } 90 | 91 | let hash_val_u64 = hasher.finish(); 92 | let hash_val_u32 = hash_val_u64 as u32; 93 | 94 | return hash_val_u32; 95 | } 96 | 97 | pub fn get_unixtime_in_nanos() -> i32{ 98 | let now = SystemTime::now(); 99 | let unixtime = now.duration_since(UNIX_EPOCH).expect("back to the future"); 100 | return unixtime.subsec_nanos() as i32; 101 | } 102 | 103 | // UNIXTIME(ナノ秒精度)にいくつか値を加算した値からアドレス文字列を生成する 104 | pub fn gen_address_str() -> String{ 105 | return (get_unixtime_in_nanos() + 10).to_string(); 106 | } 107 | 108 | pub fn overflow_check_and_conv(id : u64) -> u32 { 109 | let mut ret_id = id; 110 | if id > gval::ID_MAX as u64 { 111 | // 1を足すのは MAX より 1大きい値が 0 となるようにするため 112 | ret_id = id - ((gval::ID_MAX + 1) as u64); 113 | } 114 | return ret_id as u32; 115 | } 116 | 117 | pub fn conv_id_to_ratio_str(id : u32) -> String { 118 | let ratio = (id as f64 / gval::ID_MAX as f64) * 100.0; 119 | return format!("{:.4}", ratio); 120 | } 121 | 122 | pub fn calc_distance_between_nodes_left_mawari(base_id : u32, target_id : u32) -> u32 { 123 | // successorが自分自身である場合に用いられる場合を考慮し、base_id と target_id が一致する場合は 124 | // 距離0と考えることもできるが、一周分を距離として返す 125 | if base_id == target_id { 126 | //return (gval::ID_SPACE_RANGE - 1) as u32; 127 | return gval::ID_SPACE_RANGE; 128 | } 129 | 130 | // 0をまたいだ場合に考えやすくするためにtarget_idを0にずらしたと考えて、 131 | // base_idを同じ数だけずらす 132 | let mut slided_base_id = base_id as i64 - target_id as i64; 133 | if slided_base_id < 0 { 134 | // マイナスの値をとった場合は値0を通り越しているので 135 | // それにあった値に置き換える 136 | slided_base_id = (gval::ID_MAX as i64) + (slided_base_id as i64); 137 | } 138 | 139 | // 0を跨いだ場合の考慮はされているのであとは単純に値の大きな方から小さな方との差 140 | // が結果となる. ここでは slided_target_id は 0 であり、slided_base_id は必ず正の値 141 | // となっているので、 slided_base_idの値を返せばよい 142 | 143 | return slided_base_id as u32; 144 | } 145 | 146 | pub fn calc_distance_between_nodes_right_mawari(base_id : u32, target_id : u32) -> u32 { 147 | // successorが自分自身である場合に用いられる場合を考慮し、base_id と target_id が一致する場合は 148 | // 距離0と考えることもできるが、一周分を距離として返す 149 | if base_id == target_id { 150 | //return gval::ID_SPACE_RANGE - 1; 151 | return gval::ID_SPACE_RANGE; 152 | } 153 | 154 | // 0をまたいだ場合に考えやすくするためにbase_idを0にずらしたと考えて、target_idを 155 | // 同じ数だけずらす 156 | let mut slided_target_id = (target_id as i64) - (base_id as i64); 157 | if slided_target_id < 0 { 158 | // マイナスの値をとった場合は値0を通り越しているので 159 | // それにあった値に置き換える 160 | slided_target_id = (gval::ID_MAX as i64) + (slided_target_id as i64); 161 | } 162 | 163 | // 0を跨いだ場合の考慮はされているので、あとは単純に値の大きな方から小さな方との差 164 | // が結果となる. ここでは slided_base_id は 0 であり、slided_target_id は必ず正の値 165 | // となっているので、 slided_target_idの値を返せばよい 166 | 167 | return slided_target_id as u32; 168 | } 169 | 170 | pub fn exist_between_two_nodes_right_mawari(from_id : u32, end_id : u32, target_id : u32) -> bool { 171 | let distance_end = calc_distance_between_nodes_right_mawari(from_id, end_id); 172 | let distance_target = calc_distance_between_nodes_right_mawari(from_id, target_id); 173 | 174 | if distance_target < distance_end { 175 | return true; 176 | } else { 177 | return false; 178 | } 179 | } 180 | 181 | // TODO: (rustr) グローバル定数を見て、ファイルに書き出すフラグが立っていたら、ファイルに書くようにする (dprint) 182 | // スレッドセーフなロガーライブラリを採用する必要がありそう??? 183 | // 最初は3ノードで動作確認をするので、その時点ではstdoutに書き出す形で問題ない 184 | pub fn dprint(print_str : &String) { 185 | let local = Local::now(); 186 | let local_naive = local.naive_local(); 187 | println!("{:?},{}", local_naive, print_str); 188 | } 189 | 190 | pub fn gen_debug_str_of_node(node_info : &node_info::NodeInfo) -> String { 191 | return node_info.born_id.to_string() + &",".to_string() + &format!("{:X}", node_info.node_id) + &",".to_string() 192 | + &conv_id_to_ratio_str(node_info.node_id); 193 | } 194 | 195 | pub fn gen_debug_str_of_data(data_id : u32) -> String { 196 | return format!("{:X}", data_id) + &",".to_string() + &conv_id_to_ratio_str(data_id); 197 | } 198 | 199 | pub fn get_node_info(self_node: ArMu) -> node_info::NodeInfo { 200 | let self_node_ref = self_node.lock().unwrap(); 201 | let ret = node_info::partial_clone_from_ref_strong_without_ftable(&self_node_ref); 202 | return ret; 203 | } 204 | 205 | pub fn iv_clone_from_ref(iv_ref: &DataIdAndValue) -> DataIdAndValue { 206 | return DataIdAndValue::new(iv_ref.data_id, iv_ref.val_str.clone()); 207 | } 208 | -------------------------------------------------------------------------------- /src/data_store.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::sync::{Arc, Mutex}; 3 | use std::cell::RefCell; 4 | use std::ops::Range; 5 | 6 | use crate::gval; 7 | use crate::chord_node; 8 | use crate::node_info; 9 | use crate::chord_util; 10 | use crate::stabilizer; 11 | use crate::router; 12 | use crate::endpoints; 13 | 14 | pub const DELETED_ENTRY_MARKING_STR : &str = "THIS_KEY_IS_DELETED"; 15 | 16 | type ArMu = Arc>; 17 | 18 | #[derive(Debug, Clone)] 19 | pub struct DataStore { 20 | // Keyはハッシュを通されたものなので元データの値とは異なる 21 | stored_data : HashMap, 22 | } 23 | 24 | impl DataStore { 25 | pub fn new() -> DataStore { 26 | let sd = HashMap::new(); 27 | DataStore {stored_data : sd} 28 | } 29 | 30 | pub fn store_one_iv(& mut self, data_id: u32, value_str: String) -> bool { 31 | let iv_entry = chord_util::DataIdAndValue::new(data_id, value_str); 32 | match self.stored_data.insert(data_id.to_string(), iv_entry){ 33 | None => { return false; } 34 | Some(_old_val) => { return true; } 35 | }; 36 | } 37 | 38 | pub fn get(&self, data_id: u32) -> Result{ 39 | match self.stored_data.get(&data_id.to_string()){ 40 | None => { 41 | return Err(chord_util::GeneralError::new("GET REQUESTED DATA IS NOT FOUND".to_string(), chord_util::ERR_CODE_DATA_TO_GET_NOT_FOUND)); 42 | } 43 | Some(data_iv) => { 44 | return Ok(chord_util::iv_clone_from_ref(&data_iv)); 45 | } 46 | } 47 | } 48 | 49 | pub fn remove_one_data(&mut self, key_id: u32){ 50 | self.stored_data.remove(&key_id.to_string()); 51 | } 52 | 53 | pub fn store_iv_with_vec(&mut self, iv_vec: Vec){ 54 | for each_iv in iv_vec { 55 | self.store_one_iv(each_iv.data_id, each_iv.val_str); 56 | } 57 | } 58 | 59 | // 自身のノードIDとpredecessorのノードIDを指定すると、自身の担当範囲外のデータを削除し、同時に削除したデータ 60 | // のリストが返る 61 | pub fn get_and_delete_iv_with_pred_self_id(&mut self, pred_id: u32, self_id: u32) -> Vec { 62 | let mut ret_vec: Vec = vec![]; 63 | for (key, value) in &self.stored_data { 64 | let data_id: u32 = key.parse().unwrap(); 65 | if chord_util::exist_between_two_nodes_right_mawari(self_id, pred_id, data_id) == true { 66 | ret_vec.push((*value).clone()); 67 | } 68 | } 69 | // ret_vecに詰めたデータを self.stored_data から削除する 70 | for entry in &ret_vec { 71 | self.remove_one_data(entry.data_id); 72 | } 73 | 74 | return ret_vec; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/gval.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::sync::atomic::{AtomicIsize, AtomicBool}; 3 | use std::sync::{Arc, Mutex}; 4 | use std::cell::RefCell; 5 | 6 | use crate::chord_node; 7 | use crate::node_info; 8 | use crate::stabilizer; 9 | use crate::router; 10 | use crate::endpoints; 11 | use crate::data_store; 12 | use crate::chord_util; 13 | 14 | type ArMu = Arc>; 15 | 16 | pub const ID_SPACE_BITS : u32 = 32; // 160 <- sha1での本来の値 17 | pub const ID_SPACE_RANGE : u32 = 0xFFFFFFFF; // 0を含めての数である点に注意 18 | pub const ID_MAX : u32 = 0xFFFFFFFF - 1; 19 | 20 | // マスターデータ相当のものは含まない 21 | pub const REPLICA_NUM : u32 = 6; 22 | pub const REPLICA_ID_DISTANCE : u32 = 0xFFFFFFFF / 8; 23 | 24 | // successor_info_listに保持するNodeInfoオブジェクトの要素数 25 | // 30ノード規模を想定し、ln(32) = 6 から、6としている 26 | pub const SUCCESSOR_INFO_LIST_LEN : i32 = 6; 27 | 28 | // 何回のstabilize_successor呼出しごとにsuccessor_info_list埋めを行うか 29 | pub const FILL_SUCC_LIST_INTERVAL_TIMES : i32 = 5; 30 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | //#![allow(dead_code)] 2 | // disables several lint warnings 3 | #![allow(unused_imports)] 4 | #![allow(unused_variables)] 5 | #![allow(non_upper_case_globals)] 6 | #![allow(unused_assignments)] 7 | #![allow(dead_code)] 8 | #![allow(non_snake_case)] 9 | #![allow(unused_must_use)] 10 | 11 | #![feature(proc_macro_hygiene)] 12 | #![feature(decl_macro)] 13 | 14 | #[macro_use] 15 | extern crate rocket; 16 | 17 | #[macro_use] 18 | extern crate lazy_static; 19 | 20 | //HTTPヘッダを生成する構造体を自動生成するためのマクロを使用可能とする 21 | //認証などを行わないのであれば必要ないかも 22 | #[macro_use] 23 | extern crate hyper; 24 | 25 | // utility macros 26 | 27 | macro_rules! ArMu_new { 28 | ($wrapped:expr) => ( 29 | Arc::new(Mutex::new($wrapped)) 30 | ); 31 | } 32 | 33 | pub mod gval; 34 | pub mod chord_node; 35 | pub mod node_info; 36 | pub mod chord_util; 37 | pub mod stabilizer; 38 | pub mod router; 39 | pub mod data_store; 40 | pub mod endpoints; 41 | 42 | type ArMu = Arc>; 43 | 44 | use std::{borrow::{Borrow, BorrowMut}, io::Write, sync::Arc, thread}; 45 | use std::cell::{RefMut, RefCell, Ref}; 46 | use std::io::{stdout, stdin}; 47 | use std::sync::{Mutex, mpsc}; 48 | use std::sync::atomic::Ordering; 49 | use std::env; 50 | use std::collections::HashMap; 51 | use std::fs::File; 52 | 53 | use reqwest::Client; 54 | use serde::{Deserialize, Serialize}; 55 | use serde_json::json; 56 | 57 | fn req_rest_api_test_inner_get() { 58 | let resp = reqwest::blocking::get("http://127.0.0.1:8000/").unwrap() 59 | .text(); 60 | //.json::>().unwrap(); 61 | println!("{:#?}", resp); 62 | } 63 | 64 | fn req_rest_api_test_inner_get_param_test() { 65 | let resp = reqwest::blocking::get("http://localhost:8000/get-param-test?param1=aaaaaa¶m2=bbbbbb").unwrap() 66 | .text(); 67 | //.json::>().unwrap(); 68 | println!("{:#?}", resp); 69 | } 70 | 71 | fn req_rest_api_test_inner_post() { 72 | let text = r#"{"node_id":100,"address_str":"kanbayashi","born_id":77,"successor_info_list":[{"node_id":100,"address_str":"kanbayashi","born_id":77,"successor_info_list":[],"predecessor_info":[],"finger_table":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]}],"predecessor_info":[{"node_id":100,"address_str":"kanbayashi","born_id":77,"successor_info_list":[{"node_id":100,"address_str":"kanbayashi","born_id":77,"successor_info_list":[],"predecessor_info":[],"finger_table":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]}],"predecessor_info":[],"finger_table":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]}],"finger_table":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]}"#; 73 | let arg_node_info = serde_json::from_str::(text).unwrap(); 74 | 75 | let client = reqwest::blocking::Client::new(); 76 | let res = client.post("http://localhost:8000/deserialize") 77 | .body(serde_json::to_string(&arg_node_info).unwrap()) 78 | .send().unwrap(); 79 | 80 | println!("{:#?}", res.text()); 81 | } 82 | 83 | fn req_rest_api_test() { 84 | println!("client mode!\n"); 85 | //req_rest_api_test_inner_post(); 86 | //req_rest_api_test_inner_get_param_test(); 87 | req_rest_api_test_inner_get(); 88 | } 89 | 90 | fn main() { 91 | //引数処理 92 | let args: Vec = env::args().collect(); 93 | if args.len() == 2 { 94 | let num: i32 = args[1].parse().unwrap(); 95 | if num == 2 { // REST client 96 | req_rest_api_test(); 97 | } 98 | }else if args.len() > 2 { 99 | let born_id: i32 = args[1].parse().unwrap(); 100 | let bind_addr: String = args[2].parse().unwrap(); 101 | let bind_port_num: i32 = args[3].parse().unwrap(); 102 | let tyukai_addr: String = args[4].parse().unwrap(); 103 | let tyukai_port_num: i32 = args[5].parse().unwrap(); 104 | let log_out_path: String = args[6].parse().unwrap(); 105 | //TODO: (rustr) ログの出力先ディレクトリのパスも受けられるようにする 106 | // ディレクトリがまだ存在しなければここの引数処理の中で作成してしまう 107 | 108 | //TODO: (rustr) ロガーライブラリは初期化時にディレクトリパスも含めて出力先を指定できるものを選びたい 109 | // (つまり、ロガーライブラリの初期化もグローバルに一度やればOK、みたいなものであればここでやる) 110 | println!("born_id={:?}, bind_addr={:?}, bind_port_num={:?}, tyukai_addr={:?}, tyukai_port_num={:?}, log_out_path={:?}", &born_id, &bind_addr, &bind_port_num, &tyukai_addr, &tyukai_port_num, &log_out_path); 111 | 112 | let node_info = ArMu_new!(node_info::NodeInfo::new()); 113 | let data_store = ArMu_new!(data_store::DataStore::new()); 114 | 115 | let node_info_api_serv = Arc::clone(&node_info); 116 | let data_store_api_serv = Arc::clone(&data_store); 117 | let bind_addr_api_serv = bind_addr.clone(); 118 | 119 | let node_info_arc_succ_th = Arc::clone(&node_info); 120 | let data_store_arc_succ_th = Arc::clone(&data_store); 121 | 122 | let node_info_arc_ftable_th = Arc::clone(&node_info); 123 | let data_store_arc_ftable_th = Arc::clone(&data_store); 124 | 125 | std::thread::spawn(move|| { 126 | endpoints::rest_api_server_start(Arc::clone(&node_info_api_serv), Arc::clone(&data_store_api_serv), bind_addr_api_serv, bind_port_num); 127 | }); 128 | 129 | std::thread::sleep(std::time::Duration::from_millis(1500 as u64)); 130 | 131 | // 仲介ノードを介してChordネットワークに参加する 132 | stabilizer::join( 133 | Arc::clone(&node_info), 134 | &(bind_addr.clone() + ":" + &bind_port_num.to_string()), 135 | &(tyukai_addr + ":" + &tyukai_port_num.to_string()), 136 | born_id 137 | ); 138 | 139 | std::thread::sleep(std::time::Duration::from_millis(500 as u64)); 140 | 141 | 142 | let mut counter = 0; 143 | let stabilize_succ_th_handle = std::thread::spawn(move|| loop{ 144 | stabilizer::stabilize_successor(Arc::clone(&node_info_arc_succ_th)); 145 | counter += 1; 146 | if counter % gval::FILL_SUCC_LIST_INTERVAL_TIMES == 0 { 147 | // successor_info_listの0番要素以降を規定数まで埋める(埋まらない場合もある) 148 | stabilizer::fill_succ_info_list(Arc::clone(&node_info_arc_succ_th)); 149 | } 150 | //std::thread::sleep(std::time::Duration::from_millis(100 as u64)); 151 | std::thread::sleep(std::time::Duration::from_millis(500 as u64)); 152 | }); 153 | 154 | let stabilize_ftable_th_handle = std::thread::spawn(move|| loop{ 155 | for idx in 1..(gval::ID_SPACE_BITS + 1){ 156 | stabilizer::stabilize_finger_table(Arc::clone(&node_info_arc_ftable_th), idx as i32); 157 | //std::thread::sleep(std::time::Duration::from_millis(50 as u64)); 158 | std::thread::sleep(std::time::Duration::from_millis(100 as u64)); 159 | } 160 | }); 161 | 162 | // std::thread::spawn(|| loop{ 163 | // std::thread::sleep(std::time::Duration::from_millis((10000) as u64)); 164 | // println!("req_rest_api_test!"); 165 | // req_rest_api_test(); 166 | // }); 167 | 168 | 169 | let mut thread_handles = vec![]; 170 | thread_handles.push(stabilize_succ_th_handle); 171 | thread_handles.push(stabilize_ftable_th_handle); 172 | 173 | 174 | // スレッド終了の待ち合わせ(終了してくるスレッドは基本的に無い) 175 | for handle in thread_handles { 176 | handle.join().unwrap(); 177 | } 178 | 179 | } 180 | 181 | } 182 | -------------------------------------------------------------------------------- /src/node_info.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | use std::cell::{RefMut, RefCell, Ref}; 3 | use serde::{Serialize, Deserialize}; 4 | 5 | use crate::gval; 6 | use crate::chord_node; 7 | use crate::chord_util; 8 | use crate::stabilizer; 9 | use crate::endpoints; 10 | use crate::data_store; 11 | use crate::router; 12 | 13 | type ArMu = Arc>; 14 | 15 | #[derive(Serialize, Deserialize)] 16 | #[derive(Debug)] 17 | pub struct NodeInfo { 18 | pub node_id : u32, 19 | pub address_str: String, 20 | // デバッグ用のID 21 | // 何ノード目として生成されたかの値 22 | pub born_id : i32, 23 | // 以下の2つはNodeInfoオブジェクトを保持. 24 | // ある時点で取得したものが保持されており、変化する場合のあるフィールド 25 | // の内容は最新の内容となっているとは限らないため注意が必要. 26 | // そのような情報が必要な場合はChordNodeオブジェクトから参照し、 27 | // 必要であれば、その際に下のフィールドにdeepcopyを設定しなおさ 28 | // なければならない. 29 | 30 | // 状況に応じて伸縮するが、インデックス0には必ず 非None な要素が入っている 31 | // ように制御する 32 | pub successor_info_list: Vec, 33 | // 要素数が0もしくは1のVecとして定義する。Noneに対応する状態はlen()の結果が0の時 34 | // 格納されている要素自体はimmutableとして扱わなければならないので注意 35 | pub predecessor_info: Vec, 36 | // NodeInfoオブジェクトを要素として持つリスト 37 | // インデックスの小さい方から狭い範囲が格納される形で保持する 38 | pub finger_table: Vec>, 39 | } 40 | 41 | // routerモジュールの中で利用する、通信量を減らすための必要な情報だけのNodeInfo 42 | #[derive(Serialize, Deserialize)] 43 | #[derive(Debug)] 44 | pub struct NodeInfoSummary { 45 | pub node_id: u32, 46 | pub succ0_id: u32, 47 | pub address_str: String 48 | } 49 | 50 | impl NodeInfo { 51 | pub fn new() -> NodeInfo { 52 | NodeInfo { 53 | node_id : 0, //TODO: node_idの初期値を-1から0に変更したので注意 54 | address_str: "".to_string(), 55 | born_id : -1, 56 | successor_info_list : Vec::new(), 57 | predecessor_info : Vec::new(), 58 | finger_table : vec![None; gval::ID_SPACE_BITS as usize] 59 | } 60 | } 61 | } 62 | 63 | 64 | // 単純にdeepcopyするとチェーン構造になっているものが全てコピーされてしまう 65 | // ため、そこの考慮を行ったデータを返す 66 | // 上述の考慮により、コピーした NodeInfoオブジェクト の successor_infoと 67 | // predecessor_info および finger_table は deepcopy の対象ではあるが、 68 | // それらには空のVecが設定される. これにより、あるノードがコピーされた NodeInfo を保持 69 | // していても、自身のpredecessor や successor は自身が保持しているそれらのNodeInfo 70 | // オブジェクトの情報から辿ることができるが、その先は辿ることは直接的にはできないことになる 71 | //(predecessor や successor の NodeInfoオブジェクトをRPCで当該ノードから取得すれば可能) 72 | // 用途としては、あるノードの NodeInfo を他のノードが取得し保持する際に利用される 73 | // ことを想定して実装されている. 74 | impl Clone for NodeInfo { 75 | fn clone(&self) -> Self { 76 | // let mut ret_node_info = NodeInfo::new(); 77 | // ret_node_info.node_id = self.node_id; 78 | // ret_node_info.address_str = self.address_str.clone(); 79 | // ret_node_info.born_id = self.born_id; 80 | // ret_node_info.successor_info_list = vec![]; 81 | // ret_node_info.predecessor_info = vec![]; 82 | //return ret_node_info; 83 | return NodeInfo{ 84 | node_id: self.node_id, 85 | address_str: self.address_str.clone(), 86 | born_id: self.born_id, 87 | successor_info_list: vec![], 88 | predecessor_info: vec![], 89 | finger_table: vec![None; gval::ID_SPACE_BITS as usize] 90 | }; 91 | } 92 | } 93 | 94 | impl Clone for NodeInfoSummary { 95 | fn clone(&self) -> Self { 96 | return NodeInfoSummary{ node_id: self.node_id, succ0_id: self.succ0_id, address_str: self.address_str.clone() }; 97 | } 98 | } 99 | 100 | // 実体の参照からコピーを作成する 101 | // cloneした場合と異なり、predecessor_info, successor_info_list, finger_table 102 | // も一段階だけは値を埋めて返す(各NodeInfoオブジェクトはcloneされたもの) 103 | pub fn partial_clone_from_ref_strong(node_info_ref: &NodeInfo) -> NodeInfo { 104 | //let mut ret_node_info = NodeInfo::new(); 105 | // ret_node_info.node_id = node_info_ref.node_id; 106 | // ret_node_info.address_str = node_info_ref.address_str.clone(); 107 | // ret_node_info.born_id = node_info_ref.born_id; 108 | let mut successor_info_list = vec![]; 109 | for each_ninfo in &node_info_ref.successor_info_list { 110 | successor_info_list.push((*each_ninfo).clone()); 111 | } 112 | let mut finger_table = vec![]; 113 | for each_ninfo in &node_info_ref.finger_table { 114 | let tmp_val = match each_ninfo { 115 | None => None, 116 | Some(val) => { 117 | let ret_val = Some((*val).clone()); 118 | ret_val 119 | } 120 | }; 121 | finger_table.push(tmp_val); 122 | } 123 | let mut predecessor_info = vec![]; 124 | for each_ninfo in &node_info_ref.predecessor_info { 125 | predecessor_info.push((*each_ninfo).clone()); 126 | } 127 | 128 | //return ret_node_info; 129 | return NodeInfo{ 130 | node_id: node_info_ref.node_id, 131 | address_str: node_info_ref.address_str.clone(), 132 | born_id: node_info_ref.born_id, 133 | successor_info_list: successor_info_list, 134 | predecessor_info: predecessor_info, 135 | finger_table: finger_table 136 | }; 137 | } 138 | 139 | // 実体の参照からコピーを作成する 140 | // cloneした場合と異なり、predecessor_info, successor_info_list 141 | // も一段階だけは値を埋めて返す(各NodeInfoオブジェクトはcloneされたもの) 142 | pub fn partial_clone_from_ref_strong_without_ftable(node_info_ref: &NodeInfo) -> NodeInfo { 143 | //let mut ret_node_info = NodeInfo::new(); 144 | // ret_node_info.node_id = node_info_ref.node_id; 145 | // ret_node_info.address_str = node_info_ref.address_str.clone(); 146 | // ret_node_info.born_id = node_info_ref.born_id; 147 | let mut successor_info_list = vec![]; 148 | for each_ninfo in &node_info_ref.successor_info_list { 149 | successor_info_list.push((*each_ninfo).clone()); 150 | } 151 | // let mut finger_table = vec![]; 152 | // for each_ninfo in &node_info_ref.finger_table { 153 | // let tmp_val = match each_ninfo { 154 | // None => None, 155 | // Some(val) => { 156 | // let ret_val = Some((*val).clone()); 157 | // ret_val 158 | // } 159 | // }; 160 | // finger_table.push(tmp_val); 161 | // } 162 | let mut predecessor_info = vec![]; 163 | for each_ninfo in &node_info_ref.predecessor_info { 164 | predecessor_info.push((*each_ninfo).clone()); 165 | } 166 | 167 | //return ret_node_info; 168 | return NodeInfo{ 169 | node_id: node_info_ref.node_id, 170 | address_str: node_info_ref.address_str.clone(), 171 | born_id: node_info_ref.born_id, 172 | successor_info_list: successor_info_list, 173 | predecessor_info: predecessor_info, 174 | finger_table: vec![] 175 | }; 176 | } 177 | 178 | pub fn gen_summary_node_info(node_info_ref: &NodeInfo) -> NodeInfoSummary { 179 | return NodeInfoSummary { node_id: node_info_ref.node_id, succ0_id: node_info_ref.successor_info_list[0].node_id, address_str: node_info_ref.address_str.clone() } 180 | } 181 | 182 | pub fn gen_node_info_from_summary(summary_ref: &NodeInfoSummary) -> NodeInfo { 183 | let mut ret_ninfo = NodeInfo::new(); 184 | ret_ninfo.node_id = summary_ref.node_id; 185 | ret_ninfo.address_str = summary_ref.address_str.clone(); 186 | return ret_ninfo; 187 | } 188 | 189 | pub fn set_pred_info(self_node: ArMu, node_info: NodeInfo){ 190 | let mut self_node_ref = self_node.lock().unwrap(); 191 | if self_node_ref.predecessor_info.len() == 0 { 192 | self_node_ref.predecessor_info.push(node_info); 193 | }else{ 194 | self_node_ref.predecessor_info[0] = node_info; 195 | } 196 | } 197 | 198 | // RPC呼出しが接続失敗やタイムアウトで終了した場合、保持しているルーティングに関する情報の各々について 199 | // 反映する 200 | pub fn handle_downed_node_info(self_node: &mut NodeInfo, target_node: &NodeInfo, err: &chord_util::GeneralError){ 201 | chord_util::dprint(&("handle_downed_node_info called!".to_string())); 202 | 203 | //successorについて 204 | if err.err_code == chord_util::ERR_CODE_HTTP_REQUEST_ERR { 205 | // successor_info_listを先頭から確認しダウンが判明したノード以外を残す 206 | let mut new_succ_info_list: Vec = vec![]; 207 | for ninfo in &self_node.successor_info_list { 208 | if ninfo.node_id != self_node.node_id && ninfo.node_id != target_node.node_id { 209 | chord_util::dprint(&("insert new successor!,".to_string() + chord_util::gen_debug_str_of_node(ninfo).as_str())); 210 | new_succ_info_list.push((*ninfo).clone()); 211 | } 212 | } 213 | self_node.successor_info_list = new_succ_info_list; 214 | } 215 | 216 | // predecessorについて 217 | if self_node.predecessor_info.len() != 0 { 218 | if err.err_code == chord_util::ERR_CODE_HTTP_REQUEST_ERR 219 | && target_node.node_id == self_node.predecessor_info[0].node_id { 220 | // predecessorであった場合は predecessor のまま設定しておくと都合が悪いので 221 | // お役御免とする 222 | self_node.predecessor_info.clear(); 223 | } 224 | } 225 | 226 | // finger tableの情報について 227 | if err.err_code == chord_util::ERR_CODE_HTTP_REQUEST_ERR { 228 | // finger_tableを先頭から辿ってダウンが判明したノードがいたらNoneに設定する 229 | for idx in 0..(gval::ID_SPACE_BITS as usize) { 230 | match &self_node.finger_table[idx] { 231 | None => { continue; } 232 | Some(ninfo) => { 233 | if ninfo.node_id == target_node.node_id { 234 | self_node.finger_table[idx] = None; 235 | } 236 | } 237 | }; 238 | } 239 | } 240 | } 241 | 242 | -------------------------------------------------------------------------------- /src/router.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | use std::borrow::{Borrow, BorrowMut}; 3 | use std::cell::{RefMut, RefCell, Ref}; 4 | use std::sync::atomic::Ordering; 5 | use std::time::Duration; 6 | 7 | use crate::gval; 8 | use crate::node_info; 9 | use crate::chord_util; 10 | use crate::stabilizer; 11 | use crate::endpoints; 12 | use crate::data_store; 13 | 14 | type ArMu = Arc>; 15 | 16 | // idで識別されるデータを担当するノードの名前解決を行う 17 | pub fn find_successor(self_node: ArMu, id : u32) -> Result { 18 | let mut self_node_ref = self_node.lock().unwrap(); 19 | let deep_cloned_self_node = node_info::partial_clone_from_ref_strong(&self_node_ref); 20 | drop(self_node_ref); 21 | 22 | chord_util::dprint(&("find_successor_1,".to_string() + chord_util::gen_debug_str_of_node(&deep_cloned_self_node).as_str() + "," 23 | + chord_util::gen_debug_str_of_data(id).as_str())); 24 | 25 | let n_dash = match find_predecessor(&deep_cloned_self_node, id){ 26 | Err(err) => { 27 | return Err(chord_util::GeneralError::new(err.message, err.err_code)); 28 | } 29 | Ok(ninfo) => ninfo 30 | }; 31 | 32 | // chord_util::dprint(&("find_successor_3,".to_string() + chord_util::gen_debug_str_of_node(&deep_cloned_self_node).as_str() + "," 33 | // + chord_util::gen_debug_str_of_node(&n_dash).as_str() + "," 34 | // + chord_util::gen_debug_str_of_node(&deep_cloned_self_node.successor_info_list[0]).as_str() + "," 35 | // + chord_util::gen_debug_str_of_data(id).as_str())); 36 | 37 | let asked_n_dash_info = match endpoints::rrpc_call__get_node_info(&n_dash.address_str) { 38 | Err(err) => { 39 | self_node_ref = self_node.lock().unwrap(); 40 | node_info::handle_downed_node_info(&mut self_node_ref, &node_info::gen_node_info_from_summary(&n_dash), &err); 41 | return Err(chord_util::GeneralError::new(err.message, err.err_code)); 42 | } 43 | Ok(got_node) => { 44 | got_node 45 | } 46 | }; 47 | 48 | return Ok(node_info::NodeInfoSummary { node_id: asked_n_dash_info.successor_info_list[0].node_id, succ0_id: 0, address_str: asked_n_dash_info.successor_info_list[0].address_str.clone()}); 49 | // match endpoints::rrpc_call__get_node_info(&asked_n_dash_info.successor_info_list[0].address_str) { 50 | // Err(err) => { 51 | // self_node_ref = self_node.lock().unwrap(); 52 | // node_info::handle_downed_node_info(&mut self_node_ref, &asked_n_dash_info.successor_info_list[0], &err); 53 | // return Err(chord_util::GeneralError::new(err.message, err.err_code)); 54 | // } 55 | // Ok(got_node) => { 56 | // return Ok(node_info::gen_summary_node_info(&got_node)); 57 | // } 58 | // }; 59 | } 60 | 61 | // id の前で一番近い位置に存在するノードを探索する 62 | pub fn find_predecessor(exnode_ni_ref: &node_info::NodeInfo, id: u32) -> Result { 63 | let mut n_dash: node_info::NodeInfoSummary = node_info::gen_summary_node_info(exnode_ni_ref); 64 | let mut n_dash_found: node_info::NodeInfoSummary = node_info::gen_summary_node_info(exnode_ni_ref); 65 | let mut is_first_cpf = true; 66 | chord_util::dprint(&("find_predecessor_1,".to_string() + chord_util::gen_debug_str_of_node(&exnode_ni_ref).as_str())); 67 | 68 | // n_dash と n_dashのsuccessorの 間に id が位置するような n_dash を見つけたら、ループを終了し n_dash を return する 69 | loop { 70 | // 1周目でも実質的に同じ値が入るようになっているので問題ない 71 | n_dash = n_dash_found; 72 | 73 | //while文の書き換えの形でできたif文 74 | if chord_util::exist_between_two_nodes_right_mawari(n_dash.node_id, n_dash.succ0_id, id) { 75 | // println!("check loop break at find_predecessor {:?} {:?}", n_dash.node_id, n_dash.successor_info_list[0].node_id); 76 | break; 77 | } 78 | 79 | // chord_util::dprint(&("find_predecessor_2,".to_string() + chord_util::gen_debug_str_of_node(exnode_ni_ref).as_str() + "," 80 | // + chord_util::gen_debug_str_of_node(&n_dash).as_str())); 81 | 82 | // 初回は自ノードへの呼出しなのでRPCのインタフェースを介さずに呼び出しを行う 83 | if is_first_cpf { 84 | n_dash_found = match closest_preceding_finger(ArMu_new!(node_info::partial_clone_from_ref_strong(exnode_ni_ref)), id) { 85 | Err(err) => { 86 | return Err(chord_util::GeneralError::new(err.message, err.err_code)); 87 | } 88 | Ok(ninfo) => ninfo 89 | }; 90 | is_first_cpf = false; 91 | } else { 92 | n_dash_found = match endpoints::rrpc_call__closest_preceding_finger(&n_dash, id){ 93 | Err(err) => { 94 | return Err(chord_util::GeneralError::new(err.message, err.err_code)); 95 | } 96 | Ok(ninfo) => ninfo 97 | }; 98 | } 99 | 100 | if n_dash_found.node_id == n_dash.node_id { 101 | // 見つかったノードが、n_dash と同じで、変わらなかった場合 102 | // 同じを経路表を用いて探索することになり、結果は同じになり無限ループと 103 | // なってしまうため、探索は継続せず、探索結果として n_dash (= n_dash_found) を返す 104 | // chord_util::dprint(&("find_predecessor_3,".to_string() + chord_util::gen_debug_str_of_node(exnode_ni_ref).as_str() + "," 105 | // + chord_util::gen_debug_str_of_node(&n_dash).as_str())); 106 | return Ok(n_dash_found.clone()); 107 | } 108 | 109 | // closelst_preceding_finger は id を通り越してしまったノードは返さない 110 | // という前提の元で以下のチェックを行う 111 | let distance_old = chord_util::calc_distance_between_nodes_right_mawari(exnode_ni_ref.node_id, n_dash.node_id); 112 | let distance_found = chord_util::calc_distance_between_nodes_right_mawari(exnode_ni_ref.node_id, n_dash_found.node_id); 113 | let distance_data_id = chord_util::calc_distance_between_nodes_right_mawari(exnode_ni_ref.node_id, id); 114 | if distance_found < distance_old && !(distance_old >= distance_data_id) { 115 | // 探索を続けていくと n_dash は id に近付いていくはずであり、それは上記の前提を踏まえると 116 | // 自ノードからはより遠い位置の値になっていくということのはずである 117 | // 従って、そうなっていなかった場合は、繰り返しを継続しても意味が無く、最悪、無限ループになってしまう 118 | // 可能性があるため、探索を打ち切り、探索結果は古いn_dashを返す. 119 | // ただし、古い n_dash が 一回目の探索の場合 self であり、同じ node_idの距離は ID_SPACE_RANGE となるようにしている 120 | // ため、上記の条件が常に成り立ってしまう. 従って、その場合は例外とする(n_dashが更新される場合は、更新されたn_dashのnode_idが 121 | // 探索対象のデータのid を通り越すことは無い) 122 | 123 | // chord_util::dprint(&("find_predecessor_4,".to_string() + chord_util::gen_debug_str_of_node(exnode_ni_ref).as_str() + "," 124 | // + chord_util::gen_debug_str_of_node(&n_dash).as_str())); 125 | 126 | return Ok(n_dash.clone()); 127 | } 128 | 129 | // chord_util::dprint(&("find_predecessor_5_n_dash_updated,".to_string() + chord_util::gen_debug_str_of_node(exnode_ni_ref).as_str() + "," 130 | // + chord_util::gen_debug_str_of_node(&n_dash).as_str() + "->" 131 | // + chord_util::gen_debug_str_of_node(&n_dash_found).as_str())); 132 | 133 | // チェックの結果問題ないので n_dashを closest_preceding_fingerで探索して得た 134 | // ノード情報は次周のループの先頭でn_dash_foundに置き換えられる 135 | } 136 | 137 | return Ok(n_dash.clone()); 138 | } 139 | 140 | // 自身の持つ経路情報をもとに, id から前方向に一番近いノードの情報を返す 141 | pub fn closest_preceding_finger(self_node: ArMu, id : u32) -> Result { 142 | // 範囲の広いエントリから探索していく 143 | // finger_tableはインデックスが小さい方から大きい方に、範囲が大きくなっていく 144 | // ように構成されているため、リバースしてインデックスの大きな方から小さい方へ 145 | // 順に見ていくようにする 146 | 147 | chord_util::dprint(&"closest_preceding_finger_start".to_string()); 148 | 149 | let mut self_node_ref = self_node.lock().unwrap(); 150 | let deep_cloned_self_node = node_info::partial_clone_from_ref_strong(&self_node_ref); 151 | drop(self_node_ref); 152 | 153 | for node_info in (&deep_cloned_self_node).finger_table.iter().rev() { 154 | let conved_node_info = match node_info { 155 | None => { 156 | chord_util::dprint(&("closest_preceding_finger_0,".to_string() + chord_util::gen_debug_str_of_node(&deep_cloned_self_node).as_str())); 157 | continue; 158 | }, 159 | Some(ni) => ni 160 | }; 161 | 162 | chord_util::dprint(&("closest_preceding_finger_1,".to_string() + chord_util::gen_debug_str_of_node(&deep_cloned_self_node).as_str() + "," 163 | + chord_util::gen_debug_str_of_node(&conved_node_info).as_str())); 164 | 165 | // テーブル内のエントリが保持しているノードのIDが自身のIDと探索対象のIDの間にあれば 166 | // それを返す 167 | // (大きな範囲を見た場合、探索対象のIDが自身のIDとエントリが保持しているノードのIDの中に含まれて 168 | // しまっている可能性が高く、エントリが保持しているノードが、探索対象のIDを飛び越してしまっている 169 | // 可能性が高いということになる。そこで探索範囲を狭めていって、飛び越さない範囲で一番近いノードを 170 | // 見つけるという処理になっていると思われる) 171 | if chord_util::exist_between_two_nodes_right_mawari(deep_cloned_self_node.node_id, id, conved_node_info.node_id) { 172 | 173 | chord_util::dprint(&("closest_preceding_finger_2,".to_string() + chord_util::gen_debug_str_of_node(&deep_cloned_self_node).as_str() + "," 174 | + chord_util::gen_debug_str_of_node(&conved_node_info).as_str())); 175 | 176 | let gnba_rslt = match endpoints::rrpc_call__get_node_info(&conved_node_info.address_str){ 177 | Err(err) => { 178 | self_node_ref = self_node.lock().unwrap(); 179 | node_info::handle_downed_node_info(&mut self_node_ref, &conved_node_info, &err); 180 | return Err(chord_util::GeneralError::new(err.message, err.err_code)); 181 | } 182 | Ok(got_node) => { 183 | return Ok(node_info::gen_summary_node_info(&got_node)); 184 | } 185 | }; 186 | } 187 | } 188 | 189 | chord_util::dprint(&"closest_preceding_finger_3".to_string()); 190 | 191 | // どんなに範囲を狭めても探索対象のIDを超えてしまうノードしか存在しなかった場合 192 | // 自身の知っている情報の中で対象を飛び越さない範囲で一番近いノードは自身という 193 | // ことになる 194 | return Ok(node_info::gen_summary_node_info(&deep_cloned_self_node)); 195 | } 196 | 197 | -------------------------------------------------------------------------------- /src/stabilizer.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | use std::cell::{RefMut, RefCell, Ref}; 3 | use std::sync::atomic::Ordering; 4 | use std::borrow::{Borrow, BorrowMut}; 5 | 6 | use crate::gval; 7 | use crate::chord_node; 8 | use crate::node_info; 9 | use crate::chord_util; 10 | use crate::endpoints; 11 | use crate::data_store; 12 | use crate::router; 13 | 14 | type ArMu = Arc>; 15 | 16 | // 経路表の情報を他ノードから強制的に設定する. 17 | // joinメソッドの中で、secondノードがfirstノードに対してのみ用いるものであり、他のケースで利用してはならない 18 | pub fn set_routing_infos_force(self_node: ArMu, predecessor_info: node_info::NodeInfo, successor_info_0: node_info::NodeInfo , ftable_enry_0: node_info::NodeInfo){ 19 | //with self.existing_node.node_info.lock_of_pred_info, self.existing_node.node_info.lock_of_succ_infos: 20 | let self_node_clone; 21 | { 22 | let mut self_node_ref = self_node.lock().unwrap(); 23 | 24 | self_node_ref.successor_info_list[0] = successor_info_0; 25 | self_node_ref.finger_table[0] = Some(ftable_enry_0); 26 | self_node_clone = (*self_node_ref).clone(); 27 | } 28 | node_info::set_pred_info(Arc::clone(&self_node), predecessor_info); 29 | } 30 | 31 | // node_addressに対応するノードに問い合わせを行い、教えてもらったノードをsuccessorとして設定する 32 | pub fn join(new_node: ArMu, self_node_address: &String, tyukai_node_address: &String, born_id: i32){ 33 | let mut new_node_ref = new_node.lock().unwrap(); 34 | 35 | // ミリ秒精度のUNIXTIMEからChordネットワーク上でのIDを決定する 36 | new_node_ref.born_id = born_id; 37 | new_node_ref.address_str = (*self_node_address).clone(); 38 | new_node_ref.node_id = chord_util::hash_str_to_int(&(chord_util::get_unixtime_in_nanos().to_string())); 39 | 40 | let mut deep_cloned_new_node = node_info::partial_clone_from_ref_strong(&new_node_ref); 41 | let mut is_second_node:bool = false; 42 | 43 | if born_id == 1 { 44 | // first_node の場合 45 | 46 | // successorとpredecessorは自身として終了する 47 | new_node_ref.successor_info_list.push(deep_cloned_new_node.clone()); 48 | new_node_ref.finger_table[0] = Some(deep_cloned_new_node.clone()); 49 | drop(deep_cloned_new_node); 50 | deep_cloned_new_node = node_info::partial_clone_from_ref_strong(&new_node_ref); 51 | drop(new_node_ref); 52 | node_info::set_pred_info(Arc::clone(&new_node), deep_cloned_new_node.clone()); 53 | 54 | println!("first_node at join: {:?}", new_node.lock().unwrap()); 55 | return; 56 | } 57 | 58 | drop(new_node_ref); 59 | 60 | // ダウンしているノードの情報が与えられることは想定しない 61 | let tyukai_node = endpoints::rrpc_call__get_node_info(tyukai_node_address).unwrap(); 62 | 63 | // 仲介ノードに自身のsuccessorになるべきノードを探してもらう 64 | chord_util::dprint(&("join_1,".to_string() + chord_util::gen_debug_str_of_node(&deep_cloned_new_node).as_str() + "," 65 | + chord_util::gen_debug_str_of_node(&tyukai_node).as_str())); 66 | let successor = endpoints::rrpc_call__find_successor(&tyukai_node, deep_cloned_new_node.node_id).unwrap(); 67 | 68 | if deep_cloned_new_node.node_id == successor.node_id { 69 | chord_util::dprint(&("join_2_5,".to_string() + chord_util::gen_debug_str_of_node(&deep_cloned_new_node).as_str() + "," 70 | + chord_util::gen_debug_str_of_node(&tyukai_node).as_str() + "," 71 | + chord_util::gen_debug_str_of_node(&deep_cloned_new_node.successor_info_list[0]).as_str() + ",FOUND_NODE_IS_SAME_WITH_SELF_NODE!!!")); 72 | } 73 | 74 | new_node_ref = new_node.lock().unwrap(); 75 | if tyukai_node.node_id == tyukai_node.successor_info_list[0].node_id { 76 | // secondノードの場合の考慮 (仲介ノードは必ずfirst node) 77 | 78 | // 2ノードでsuccessorでもpredecessorでも、チェーン構造で正しい環が構成されるよう強制的に全て設定してしまう 79 | // secondノードの場合の考慮 (仲介ノードは必ずfirst node) 80 | is_second_node = true; 81 | 82 | new_node_ref.successor_info_list.push(tyukai_node.clone()); 83 | 84 | drop(deep_cloned_new_node); 85 | deep_cloned_new_node = node_info::partial_clone_from_ref_strong(&new_node_ref); 86 | drop(new_node_ref); 87 | node_info::set_pred_info(Arc::clone(&new_node), tyukai_node.clone()); 88 | endpoints::rrpc_call__set_routing_infos_force( 89 | &tyukai_node, 90 | deep_cloned_new_node.clone(), 91 | deep_cloned_new_node.clone(), 92 | deep_cloned_new_node.clone() 93 | ); 94 | 95 | chord_util::dprint(&("join_3,".to_string() + chord_util::gen_debug_str_of_node(&deep_cloned_new_node).as_str() + "," 96 | + chord_util::gen_debug_str_of_node(&tyukai_node).as_str() + "," 97 | + chord_util::gen_debug_str_of_node(&deep_cloned_new_node.successor_info_list[0]).as_str())); 98 | 99 | return; 100 | }else{ 101 | drop(new_node_ref); 102 | } 103 | 104 | new_node_ref = new_node.lock().unwrap(); 105 | 106 | // successorを設定する 107 | new_node_ref.successor_info_list.push(node_info::gen_node_info_from_summary(&successor)); 108 | 109 | // finger_tableのインデックス0は必ずsuccessorになるはずなので、設定しておく 110 | new_node_ref.finger_table[0] = Some(new_node_ref.successor_info_list[0].clone()); 111 | 112 | // successorと、successorノードの情報だけ適切なものとする 113 | 114 | drop(new_node_ref); 115 | match endpoints::rrpc_call__check_predecessor(&node_info::gen_node_info_from_summary(&successor), &deep_cloned_new_node.clone()){ 116 | Err(err) => { 117 | // IDを変えてリトライ 118 | // (これで異なるsuccessorが得られて、そのノードは生きていることを期待する) 119 | join(new_node, self_node_address, tyukai_node_address, born_id); 120 | } 121 | Ok(some) => {} 122 | }; 123 | } 124 | 125 | pub fn stabilize_successor(self_node: ArMu) -> Result{ 126 | let mut self_node_ref = self_node.lock().unwrap(); 127 | let mut deep_cloned_self_node = node_info::partial_clone_from_ref_strong(&self_node_ref); 128 | //println!("P-SELF-S: P: {:?} SELF: {:?} S {:?}", self_node_ref.predecessor_info, *self_node_ref, self_node_ref.successor_info_list); 129 | drop(self_node_ref); 130 | 131 | chord_util::dprint(&("stabilize_successor_1,".to_string() + chord_util::gen_debug_str_of_node(&deep_cloned_self_node).as_str() + "," 132 | + chord_util::gen_debug_str_of_node(&deep_cloned_self_node.successor_info_list[0]).as_str())); 133 | 134 | // firstノードだけが存在する状況で、self_nodeがfirst_nodeであった場合に対する考慮 135 | if deep_cloned_self_node.predecessor_info.len() == 0 && deep_cloned_self_node.node_id == deep_cloned_self_node.successor_info_list[0].node_id { 136 | chord_util::dprint(&("stabilize_successor_1_5,".to_string() + chord_util::gen_debug_str_of_node(&deep_cloned_self_node).as_str() + "," 137 | + chord_util::gen_debug_str_of_node(&deep_cloned_self_node.successor_info_list[0]).as_str())); 138 | 139 | // secondノードがjoinしてきた際にチェーン構造は2ノードで適切に構成されるように 140 | // なっているため、ここでは何もせずに終了する 141 | 142 | return Ok(true); 143 | } 144 | 145 | // 自身のsuccessorに、当該ノードが認識しているpredecessorを訪ねる 146 | // 自身が保持している successor_infoのミュータブルなフィールドは最新の情報でない 147 | // 場合があるため、successorのChordNodeオブジェクトを引いて、そこから最新のnode_info 148 | // の参照を得る 149 | 150 | let ret = endpoints::rrpc_call__get_node_info(&deep_cloned_self_node.successor_info_list[0].address_str); 151 | 152 | let successor_info = match ret{ 153 | Err(err) => { 154 | self_node_ref = self_node.lock().unwrap(); 155 | node_info::handle_downed_node_info(&mut self_node_ref, &deep_cloned_self_node.successor_info_list[0], &err); 156 | return Err(chord_util::GeneralError::new(err.message, err.err_code)); 157 | } 158 | Ok(got_node) => { 159 | got_node 160 | } 161 | }; 162 | 163 | if successor_info.predecessor_info.len() == 0 { 164 | //is_successor_has_no_pred = true; 165 | chord_util::dprint(&("stabilize_successor_2,".to_string() + chord_util::gen_debug_str_of_node(&deep_cloned_self_node).as_str() + "," 166 | + chord_util::gen_debug_str_of_node(&deep_cloned_self_node.successor_info_list[0]).as_str())); 167 | 168 | match endpoints::rrpc_call__check_predecessor(&successor_info, &deep_cloned_self_node.clone()){ 169 | Err(err) => { 170 | self_node_ref = self_node.lock().unwrap(); 171 | node_info::handle_downed_node_info(&mut self_node_ref, &successor_info, &err); 172 | return Ok(true); 173 | } 174 | Ok(some) => {} 175 | }; 176 | 177 | return Ok(true); 178 | } 179 | 180 | chord_util::dprint(&("stabilize_successor_3,".to_string() + chord_util::gen_debug_str_of_node(&deep_cloned_self_node).as_str() + "," 181 | + chord_util::gen_debug_str_of_node(&successor_info.successor_info_list[0]).as_str())); 182 | 183 | let pred_id_of_successor = successor_info.predecessor_info[0].node_id; 184 | 185 | chord_util::dprint(&("stabilize_successor_3_5,".to_string() + &format!("{:X}", pred_id_of_successor))); 186 | 187 | // 下のパターン1から3という記述は以下の資料による説明に基づく 188 | // https://www.slideshare.net/did2/chorddht 189 | if pred_id_of_successor == deep_cloned_self_node.node_id { 190 | // パターン1 191 | // 特に訂正は不要なので処理を終了する 192 | chord_util::dprint(&("stabilize_successor_4,".to_string() + chord_util::gen_debug_str_of_node(&deep_cloned_self_node).as_str() + "," 193 | + chord_util::gen_debug_str_of_node(&successor_info.successor_info_list[0]).as_str())); 194 | return Ok(true); 195 | }else{ 196 | // 以下、パターン2およびパターン3に対応する処理 197 | 198 | // 自身がsuccessorにとっての正しいpredecessorでないか確認を要請し必要であれば 199 | // 情報を更新してもらう 200 | // 事前チェックによって避けられるかもしれないが、常に実行する 201 | 202 | chord_util::dprint(&("stabilize_successor_5,".to_string() + chord_util::gen_debug_str_of_node(&deep_cloned_self_node).as_str() + "," 203 | + chord_util::gen_debug_str_of_node(&successor_info.successor_info_list[0]).as_str())); 204 | 205 | match endpoints::rrpc_call__check_predecessor(&successor_info, &deep_cloned_self_node.clone()){ 206 | Err(err) => { 207 | self_node_ref = self_node.lock().unwrap(); 208 | node_info::handle_downed_node_info(&mut self_node_ref, &successor_info, &err); 209 | return Ok(true); 210 | } 211 | Ok(some) => {} 212 | }; 213 | 214 | let distance_unknown = chord_util::calc_distance_between_nodes_left_mawari(successor_info.node_id, pred_id_of_successor); 215 | let distance_me = chord_util::calc_distance_between_nodes_left_mawari(successor_info.node_id, deep_cloned_self_node.node_id); 216 | chord_util::dprint(&("stabilize_successor distance_unknown=".to_string() 217 | + distance_unknown.to_string().as_str() 218 | + " distance_me=" + distance_me.to_string().as_str()) 219 | ); 220 | if distance_unknown < distance_me { 221 | // successorの認識しているpredecessorが自身ではなく、かつ、そのpredecessorが 222 | // successorから自身に対して前方向にたどった場合の経路中に存在する場合 223 | // 自身の認識するsuccessorの情報を更新する 224 | 225 | self_node_ref = self_node.lock().unwrap(); 226 | chord_util::dprint(&("stabilize_successor_SET_SUCCESSOR,".to_string() + chord_util::gen_debug_str_of_node(&deep_cloned_self_node).as_str() + ", from " 227 | + chord_util::gen_debug_str_of_node(&self_node_ref.successor_info_list[0]).as_str() + " to " 228 | + chord_util::gen_debug_str_of_node(&successor_info.predecessor_info[0]).as_str())); 229 | self_node_ref.successor_info_list[0] = successor_info.predecessor_info[0].clone(); 230 | deep_cloned_self_node = node_info::partial_clone_from_ref_strong(&self_node_ref); 231 | drop(self_node_ref); 232 | 233 | // 新たなsuccessorに対して自身がpredecessorでないか確認を要請し必要であれ 234 | // ば情報を更新してもらう 235 | let new_successor_info = match endpoints::rrpc_call__get_node_info(&deep_cloned_self_node.successor_info_list[0].address_str){ 236 | Err(err) => { 237 | self_node_ref = self_node.lock().unwrap(); 238 | node_info::handle_downed_node_info(&mut self_node_ref, &deep_cloned_self_node.successor_info_list[0], &err); 239 | return Err(chord_util::GeneralError::new(err.message, err.err_code)); 240 | } 241 | Ok(got_node) => { 242 | got_node 243 | } 244 | }; 245 | 246 | match endpoints::rrpc_call__check_predecessor(&new_successor_info, &deep_cloned_self_node.clone()){ 247 | Err(err) => { 248 | self_node_ref = self_node.lock().unwrap(); 249 | node_info::handle_downed_node_info(&mut self_node_ref, &new_successor_info, &err); 250 | return Ok(true); 251 | } 252 | Ok(some) => {} 253 | }; 254 | 255 | chord_util::dprint(&("stabilize_successor_6,".to_string() + chord_util::gen_debug_str_of_node(&deep_cloned_self_node).as_str() + "," 256 | + chord_util::gen_debug_str_of_node(&deep_cloned_self_node.successor_info_list[0]).as_str() + "," 257 | + chord_util::gen_debug_str_of_node(&deep_cloned_self_node).as_str())); 258 | 259 | return Ok(true); 260 | } 261 | } 262 | 263 | return Ok(true); 264 | } 265 | 266 | // successor_info_listのインデックス1より後ろを規定数まで埋める 267 | // 途中でエラーとなった場合は、規定数に届いていなくとも処理を中断する 268 | pub fn fill_succ_info_list(self_node: ArMu) -> Result{ 269 | let mut self_node_ref = self_node.lock().unwrap(); 270 | chord_util::dprint(&("fill_succ_info_list_0,".to_string() + chord_util::gen_debug_str_of_node(&self_node_ref).as_str())); 271 | 272 | let self_node_id = self_node_ref.node_id; 273 | let mut next_succ_id = self_node_ref.node_id; 274 | 275 | let first_succ = self_node_ref.successor_info_list[0].clone(); 276 | //let mut next_succ_info = node_info::partial_clone_from_ref_strong(&self_node_ref); 277 | drop(self_node_ref); 278 | let mut next_succ_info = match endpoints::rrpc_call__get_node_info(&first_succ.address_str) { 279 | Err(err) => { 280 | self_node_ref = self_node.lock().unwrap(); 281 | node_info::handle_downed_node_info(&mut self_node_ref, &first_succ, &err); 282 | // 後続を辿れないので、リスト埋めは中止してreturnする 283 | return Err(err); 284 | } 285 | Ok(ninfo) => ninfo 286 | }; 287 | 288 | let mut idx_counter = 1; 289 | for times in 1..(gval::SUCCESSOR_INFO_LIST_LEN){ 290 | next_succ_id = next_succ_info.node_id; 291 | next_succ_info = node_info::partial_clone_from_ref_strong(&next_succ_info.successor_info_list[0]); 292 | if next_succ_info.node_id == self_node_id || next_succ_info.node_id == next_succ_id { 293 | // next_succ_infoがself_nodeと同一もしくは、1つ前の位置のノードを指していた場合 294 | // 後続を辿っていく処理がループを構成してしまうため抜ける 295 | self_node_ref = self_node.lock().unwrap(); 296 | chord_util::dprint( 297 | &("fill_succ_info_list_1,".to_string() 298 | + chord_util::gen_debug_str_of_node(&self_node_ref).as_str() + "," 299 | + chord_util::gen_debug_str_of_node(&next_succ_info).as_str() + "," 300 | + self_node_ref.successor_info_list.len().to_string().as_str() 301 | )); 302 | return Ok(true); 303 | } 304 | self_node_ref = self_node.lock().unwrap(); 305 | if self_node_ref.successor_info_list.len() < (idx_counter + 1) { 306 | self_node_ref.successor_info_list.push(next_succ_info.clone()); 307 | chord_util::dprint( 308 | &("fill_succ_info_list_2,".to_string() 309 | + chord_util::gen_debug_str_of_node(&self_node_ref).as_str() + "," 310 | + chord_util::gen_debug_str_of_node(&next_succ_info).as_str() + "," 311 | + self_node_ref.successor_info_list.len().to_string().as_str() 312 | )); 313 | }else{ 314 | self_node_ref.successor_info_list[idx_counter] = next_succ_info.clone(); 315 | chord_util::dprint( 316 | &("fill_succ_info_list_3,".to_string() 317 | + chord_util::gen_debug_str_of_node(&self_node_ref).as_str() + "," 318 | + chord_util::gen_debug_str_of_node(&next_succ_info).as_str() + "," 319 | + self_node_ref.successor_info_list.len().to_string().as_str() + "," 320 | + idx_counter.to_string().as_str() 321 | )); 322 | } 323 | idx_counter += 1; 324 | drop(self_node_ref); 325 | next_succ_info = match endpoints::rrpc_call__get_node_info(&next_succ_info.address_str) { 326 | Err(err) => { 327 | self_node_ref = self_node.lock().unwrap(); 328 | node_info::handle_downed_node_info(&mut self_node_ref, &next_succ_info, &err); 329 | // 後続を辿れないので、リスト埋めは中止してreturnする 330 | return Err(err); 331 | } 332 | Ok(ninfo) => ninfo 333 | }; 334 | } 335 | 336 | return Ok(true); 337 | } 338 | 339 | // FingerTableに関するstabilize処理を行う 340 | // 一回の呼び出しで1エントリを更新する 341 | // FingerTableのエントリはこの呼び出しによって埋まっていく 342 | pub fn stabilize_finger_table(self_node: ArMu, idx: i32) -> Result { 343 | let mut self_node_ref = self_node.lock().unwrap(); 344 | let self_node_deep_cloned = node_info::partial_clone_from_ref_strong(&self_node_ref); 345 | //chord_util::dprint_routing_info(self.existing_node, sys._getframe().f_code.co_name); 346 | 347 | chord_util::dprint(&("stabilize_finger_table_1,".to_string() + chord_util::gen_debug_str_of_node(&self_node_ref).as_str())); 348 | 349 | // FingerTableの各要素はインデックスを idx とすると 2^IDX 先のIDを担当する、もしくは 350 | // 担当するノードに最も近いノードが格納される 351 | let update_id = chord_util::overflow_check_and_conv((self_node_ref.node_id as u64) + (2u64.pow(idx as u32) as u64)); 352 | 353 | println!("update_id: {:?} {:?}", update_id, idx); 354 | 355 | drop(self_node_ref); 356 | //let find_rslt = endpoints::rrpc_call__find_successor(&self_node_deep_cloned, update_id); 357 | let find_rslt = router::find_successor(Arc::clone(&self_node), update_id); 358 | 359 | self_node_ref = self_node.lock().unwrap(); 360 | match find_rslt { 361 | Err(err) => { 362 | // 適切な担当ノードを得ることができなかった 363 | // 今回のエントリの更新はあきらめるが、例外の発生原因はおおむね見つけたノードがダウンしていた 364 | // ことであるので、更新対象のエントリには None を設定しておく 365 | self_node_ref.finger_table[(idx - 1) as usize] = None; 366 | chord_util::dprint(&("stabilize_finger_table_2_5,NODE_IS_DOWNED,".to_string() 367 | + chord_util::gen_debug_str_of_node(&self_node_ref).as_str())); 368 | 369 | node_info::handle_downed_node_info(&mut self_node_ref, &self_node_deep_cloned, &err); 370 | 371 | return Ok(true); 372 | }, 373 | Ok(found_node) => { 374 | self_node_ref.finger_table[(idx - 1) as usize] = Some(node_info::gen_node_info_from_summary(&found_node)); 375 | 376 | // chord_util::dprint(&("stabilize_finger_table_3,".to_string() 377 | // + chord_util::gen_debug_str_of_node(&self_node_ref).as_str() + "," 378 | // + chord_util::gen_debug_str_of_node(&found_node).as_str())); 379 | 380 | return Ok(true); 381 | } 382 | } 383 | } 384 | 385 | // caller_node が自身の正しい predecessor でないかチェックし、そうであった場合、経路表の情報を更新する 386 | // 本メソッドはstabilize処理の中で用いられる 387 | pub fn check_predecessor(self_node: ArMu, data_store: ArMu, caller_node_ni: node_info::NodeInfo) -> Result { 388 | let mut self_node_ref = self_node.lock().unwrap(); 389 | let self_node_deep_cloned = node_info::partial_clone_from_ref_strong(&self_node_ref); 390 | drop(self_node_ref); 391 | 392 | chord_util::dprint(&("check_predecessor_1,".to_string() + chord_util::gen_debug_str_of_node(&self_node_deep_cloned).as_str() + "," 393 | + chord_util::gen_debug_str_of_node(&caller_node_ni).as_str() + "," 394 | + chord_util::gen_debug_str_of_node(&self_node_deep_cloned.successor_info_list[0]).as_str())); 395 | 396 | if self_node_deep_cloned.predecessor_info.len() == 0 { 397 | // predecesorが設定されていなければ無条件にチェックを求められたノードを設定する 398 | node_info::set_pred_info(Arc::clone(&self_node), caller_node_ni.clone()); 399 | chord_util::dprint(&("check_predecessor_1,".to_string() + chord_util::gen_debug_str_of_node(&self_node_deep_cloned).as_str() + "," 400 | + chord_util::gen_debug_str_of_node(&caller_node_ni).as_str() + "," 401 | + chord_util::gen_debug_str_of_node(&self_node_deep_cloned.successor_info_list[0]).as_str())); 402 | return Ok(true); 403 | } 404 | 405 | // predecessorの生死チェックを行い、ダウンしていた場合 未設定状態に戻して return する 406 | // (本来 check_predecessor でやる処理ではないと思われるが、finger tableの情報を用いて 407 | // ノードダウン時の対処を行う場合に、このコードがないとうまくいかなそうなのでここで処理) 408 | match endpoints::rrpc_call__get_node_info(&self_node_deep_cloned.predecessor_info[0].address_str){ 409 | Err(err) => { 410 | self_node_ref = self_node.lock().unwrap(); 411 | node_info::handle_downed_node_info(&mut self_node_ref, &self_node_deep_cloned.predecessor_info[0], &err); 412 | return Ok(true); 413 | } 414 | Ok(some) => {} 415 | } 416 | 417 | chord_util::dprint(&("check_predecessor_2,".to_string() + chord_util::gen_debug_str_of_node(&self_node_deep_cloned).as_str() + "," 418 | + chord_util::gen_debug_str_of_node(&self_node_deep_cloned.successor_info_list[0]).as_str())); 419 | 420 | self_node_ref = self_node.lock().unwrap(); 421 | let distance_check = chord_util::calc_distance_between_nodes_left_mawari(self_node_ref.node_id, caller_node_ni.node_id); 422 | let distance_cur = chord_util::calc_distance_between_nodes_left_mawari(self_node_ref.node_id, 423 | self_node_ref.predecessor_info[0].node_id); 424 | chord_util::dprint(&("check_predecessor distance_check=".to_string() 425 | + distance_check.to_string().as_str() 426 | + " distance_cur=" + distance_cur.to_string().as_str()) 427 | ); 428 | // 確認を求められたノードの方が現在の predecessor より predecessorらしければ 429 | // 経路表の情報を更新する 430 | if distance_check < distance_cur { 431 | drop(self_node_ref); 432 | node_info::set_pred_info(Arc::clone(&self_node), caller_node_ni.clone()); 433 | 434 | // 切り替えたpredecessorに対してデータの委譲を行う 435 | let self_id = self_node_deep_cloned.node_id; 436 | let new_pred_id = caller_node_ni.node_id; 437 | let mut data_store_ref = data_store.lock().unwrap(); 438 | let delegate_datas: Vec; 439 | delegate_datas = data_store_ref.get_and_delete_iv_with_pred_self_id(new_pred_id, self_id); 440 | drop(data_store_ref); 441 | 442 | self_node_ref = self_node.lock().unwrap(); 443 | chord_util::dprint(&("check_predecessor_3,".to_string() + chord_util::gen_debug_str_of_node(&self_node_ref).as_str() + "," 444 | + chord_util::gen_debug_str_of_node(&self_node_ref.successor_info_list[0]).as_str() + "," 445 | + chord_util::gen_debug_str_of_node(&self_node_ref.predecessor_info[0]).as_str())); 446 | drop(self_node_ref); 447 | 448 | match endpoints::rrpc_call__pass_datas(&caller_node_ni, delegate_datas){ 449 | Err(err) => { 450 | self_node_ref = self_node.lock().unwrap(); 451 | node_info::handle_downed_node_info(&mut self_node_ref, &caller_node_ni, &err); 452 | return Ok(true); 453 | } 454 | Ok(some) => { return Ok(true) } 455 | } 456 | } 457 | return Ok(true); 458 | } 459 | 460 | // passed_datasで渡されたデータのリストを自身のDataStoreに加える 461 | // 基本的に、ノード参加が判明した際に他のノードが self_node に対してデータを委譲 462 | // する際に利用することを想定する 463 | pub fn pass_datas(self_node: ArMu, data_store: ArMu, pass_datas: Vec) -> Result { 464 | let mut data_store_ref = data_store.lock().unwrap(); 465 | data_store_ref.store_iv_with_vec(pass_datas); 466 | 467 | return Ok(true); 468 | } 469 | 470 | -------------------------------------------------------------------------------- /tools/dkvs_client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "flag" 7 | "fmt" 8 | "io/ioutil" 9 | "net/http" 10 | "os/exec" 11 | "runtime" 12 | "strconv" 13 | "time" 14 | ) 15 | 16 | func test_get_request_which_has_query_string() { 17 | url := "http://localhost:8000/get-param-test?param1=aaaaaa¶m2=bbbbbb" 18 | // TODO: クエリストリングでパラメータを渡す際にURIエンコードが行われるか確認して 19 | // されないようであればされるようにする(方法を確認しておく)必要あり 20 | req, _ := http.NewRequest("GET", url, nil) 21 | 22 | client := new(http.Client) 23 | resp, _ := client.Do(req) 24 | defer resp.Body.Close() 25 | 26 | byteArray, _ := ioutil.ReadAll(resp.Body) 27 | fmt.Println(string(byteArray)) 28 | } 29 | 30 | func test_post_request_deserialize() error { 31 | url := "http://localhost:8000/deserialize" 32 | jsonStr := `{"node_id":100,"address_str":"kanbayashi","born_id":77,"successor_info_list":[{"node_id":100,"address_str":"kanbayashi","born_id":77,"successor_info_list":[],"predecessor_info":[],"finger_table":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]}],"predecessor_info":[{"node_id":100,"address_str":"kanbayashi","born_id":77,"successor_info_list":[{"node_id":100,"address_str":"kanbayashi","born_id":77,"successor_info_list":[],"predecessor_info":[],"finger_table":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]}],"predecessor_info":[],"finger_table":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]}],"finger_table":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]}` 33 | 34 | req, err := http.NewRequest( 35 | "POST", 36 | url, 37 | bytes.NewBuffer([]byte(jsonStr)), 38 | ) 39 | if err != nil { 40 | return err 41 | } 42 | 43 | // Content-Type 設定 44 | req.Header.Set("Content-Type", "application/json") 45 | 46 | client := &http.Client{} 47 | resp, err := client.Do(req) 48 | if err != nil { 49 | return err 50 | } 51 | defer resp.Body.Close() 52 | 53 | byteArray, _ := ioutil.ReadAll(resp.Body) 54 | 55 | fmt.Println(string(byteArray)) 56 | 57 | return err 58 | } 59 | 60 | func test_get_request_Result_type_return() { 61 | url := "http://127.0.0.1:8000/result-type" 62 | // TODO: クエリストリングでパラメータを渡す際にURIエンコードが行われるか確認して 63 | // されないようであればされるようにする(方法を確認しておく)必要あり 64 | req, _ := http.NewRequest("GET", url, nil) 65 | 66 | client := new(http.Client) 67 | resp, _ := client.Do(req) 68 | defer resp.Body.Close() 69 | 70 | byteArray, _ := ioutil.ReadAll(resp.Body) 71 | 72 | fmt.Println(string(byteArray)) 73 | } 74 | 75 | // このプログラムがtoolsディレクトリ直下で実行されている前提 76 | func test_process_exec() { 77 | err := exec.Command("../target/debug/rust_dkvs", "3", "5", "100501").Start() 78 | //out, err := exec.Command("../target/debug/rust_dkvs", "3", "5", "100501").Output() 79 | if err != nil { 80 | fmt.Println(err) 81 | } 82 | //fmt.Printf("%s\n", out) 83 | } 84 | 85 | func http_get_request(addr_and_port string, path_str string) (map[string]interface{}, error) { 86 | url := "http://" + addr_and_port + path_str 87 | // TODO: クエリストリングでパラメータを渡す際にURIエンコードが行われるか確認して 88 | // されないようであればされるようにする(方法を確認しておく)必要あり 89 | req, _ := http.NewRequest("GET", url, nil) 90 | 91 | client := new(http.Client) 92 | resp, err := client.Do(req) 93 | if err != nil { 94 | return nil, err 95 | } 96 | defer resp.Body.Close() 97 | 98 | byteArray, _ := ioutil.ReadAll(resp.Body) 99 | 100 | // JSONデコード 101 | var decoded_data interface{} 102 | if err := json.Unmarshal(byteArray, &decoded_data); err != nil { 103 | fmt.Println(err) 104 | } 105 | 106 | return decoded_data.(map[string]interface{}), err 107 | } 108 | 109 | func extract_addr_and_born_id(input_json map[string]interface{}) (string, int32, uint32, string, string) { 110 | ret_addr := input_json["address_str"].(string) 111 | ret_born_id := int32(input_json["born_id"].(float64)) 112 | ret_node_id := uint32(input_json["node_id"].(float64)) 113 | succ_list := input_json["successor_info_list"].([]interface{}) 114 | ret_self_addr := input_json["address_str"].(string) 115 | succ_entry_0 := succ_list[0].(map[string]interface{}) 116 | ret_succ_addr := succ_entry_0["address_str"].(string) 117 | return ret_addr, ret_born_id, ret_node_id, ret_self_addr, ret_succ_addr 118 | } 119 | 120 | const bind_ip_addr = "127.0.0.1" 121 | const check_node_limit = 150 122 | 123 | var platform string 124 | 125 | // func check_chain_with_successor_info() { 126 | // const endpoint_path = "/get_node_info" 127 | // start_port := 11000 128 | // start_addr := bind_ip_addr + ":" + strconv.Itoa(start_port) 129 | 130 | // succ_addr := start_addr 131 | // cur_addr := "" 132 | // born_id := -1.0 133 | // node_id := -1.0 134 | // counter := 0 135 | // request_count := 0 136 | // is_success_reqest := false 137 | // for true { 138 | // resp_json, err := http_get_request(succ_addr, endpoint_path) 139 | // request_count++ 140 | // if request_count == check_node_limit { 141 | // fmt.Println("Error: travarse times may exceeded launched nodes!") 142 | // break 143 | // } 144 | // if err != nil { 145 | // if is_success_reqest == false { 146 | // start_port += 1 147 | // succ_addr = bind_ip_addr + ":" + strconv.Itoa(start_port) 148 | // continue 149 | // } else { 150 | // fmt.Println("Error: successor should downed and information of successor is not recovered.") 151 | // break 152 | // } 153 | // } 154 | // is_success_reqest = true 155 | // cur_addr, born_id, node_id, succ_addr = extract_addr_and_born_id(resp_json) 156 | // counter++ 157 | // fmt.Printf("addr=%s born_id=%f node_id_ratio=%f counter=%d succ_addr=%s\n", cur_addr, born_id, (node_id/0xFFFFFFFF)*100.0, counter, succ_addr) 158 | // if succ_addr == start_addr { 159 | // break 160 | // } 161 | // } 162 | // } 163 | 164 | func check_chain_with_successor_info() { 165 | const endpoint_path = "/get_node_info" 166 | start_port := 11000 167 | start_addr := bind_ip_addr + ":" + strconv.Itoa(start_port) 168 | 169 | succ_addr := start_addr 170 | self_addr := start_addr 171 | cur_addr := "" 172 | var born_id int32 = -1 173 | var node_id uint32 = 1 174 | counter := 0 175 | request_count := 0 176 | is_success_request := false 177 | is_fin := false 178 | fin: 179 | for !is_fin { 180 | var err error 181 | var retry_count = 0 182 | var resp_json map[string]interface{} = nil 183 | for { 184 | resp_json, err = http_get_request(succ_addr, endpoint_path) 185 | request_count++ 186 | if request_count == check_node_limit { 187 | fmt.Println("Error: travarse times may exceeded launched nodes!") 188 | is_fin = true 189 | break fin 190 | } 191 | if err != nil { 192 | if !is_success_request { 193 | start_port += 1 194 | succ_addr = bind_ip_addr + ":" + strconv.Itoa(start_port) 195 | break 196 | } else if retry_count < 3 { 197 | //同じアドレスでもう一回 198 | retry_count++ 199 | continue 200 | } else { 201 | fmt.Println("Error: successor should downed and information of successor is not recovered.") 202 | is_fin = true 203 | break fin 204 | } 205 | } 206 | is_success_request = true 207 | break 208 | } 209 | retry_count = 0 210 | if is_success_request { 211 | cur_addr, born_id, node_id, self_addr, succ_addr = extract_addr_and_born_id(resp_json) 212 | counter++ 213 | fmt.Printf("addr=%s node_id=%d born_id=%d node_id_ratio=%f counter=%d self_addr=%s succ_addr=%s\n", cur_addr, node_id, born_id, (float64(node_id)/float64(0xFFFFFFFF))*float64(100.0), counter, self_addr, succ_addr) 214 | if succ_addr == start_addr { 215 | break 216 | } 217 | } 218 | } 219 | } 220 | 221 | func start_a_node(born_id int, bind_addr string, bind_port int, tyukai_addr string, tyukai_port int, log_dir string) { 222 | shortcut_script := "" 223 | if platform == "windows" { 224 | shortcut_script = "rust_dkvs.bat" 225 | } else { 226 | shortcut_script = "./rust_dkvs.sh" 227 | } 228 | 229 | err := exec.Command( 230 | shortcut_script, //"rust_dkvs.bat", //"../target/debug/rust_dkvs", 231 | strconv.Itoa(born_id), 232 | bind_addr, 233 | strconv.Itoa(bind_port), 234 | tyukai_addr, 235 | strconv.Itoa(tyukai_port), 236 | log_dir).Start() 237 | if err != nil { 238 | fmt.Println(err) 239 | } 240 | } 241 | 242 | func setup_nodes(num int) { 243 | start_port := 11000 244 | cur_port := start_port 245 | for ii := 0; ii < num; ii++ { 246 | start_a_node(ii+1, bind_ip_addr, cur_port+ii, bind_ip_addr, start_port, "./") 247 | fmt.Printf("launched born_id=%d\n", ii+1) 248 | time.Sleep(time.Second * 3) 249 | } 250 | } 251 | 252 | func global_put_simple(addr_and_port string, key string, val string) (map[string]interface{}, error) { 253 | return http_get_request(addr_and_port, "/global_put_simple?key="+key+"&val="+val) 254 | } 255 | 256 | func global_get_simple(addr_and_port string, key string) (map[string]interface{}, error) { 257 | return http_get_request(addr_and_port, "/global_get_simple?key="+key) 258 | } 259 | 260 | // 固定されたテスト用の keyとvalueの組み合わせを global_putする 261 | func put_test_values(addr_and_port string) { 262 | for ii := 0; ii < 100; ii++ { 263 | key := strconv.Itoa(ii) 264 | val := key 265 | fmt.Printf("put request key=%s\n", key) 266 | _, err := global_put_simple(addr_and_port, key, val) 267 | if err != nil { 268 | fmt.Println("global_put_simple request failed:" + err.Error()) 269 | } 270 | } 271 | } 272 | 273 | func get_test_values(addr_and_port string, reverse bool) { 274 | num := 100 275 | start_unix_time := time.Now().Unix() 276 | for ii := 0; ii < num; ii++ { 277 | var key string 278 | if reverse { 279 | key = strconv.Itoa(num - ii - 1) 280 | } else { 281 | key = strconv.Itoa(ii) 282 | } 283 | 284 | fmt.Printf("get request key=%s\n", key) 285 | resp_json, err := global_get_simple(addr_and_port, key) 286 | fmt.Println(resp_json) 287 | if err != nil { 288 | fmt.Printf("get missed key=%s\n", key) 289 | } 290 | } 291 | end_unitx_time := time.Now().Unix() 292 | time_to_get := float64(end_unitx_time-start_unix_time) / float64(num) 293 | fmt.Printf("%f sec/data\n", time_to_get) 294 | } 295 | 296 | func get_test_values_parallel() { 297 | num := 100 * 20 298 | 299 | ch1 := make(chan bool) 300 | ch11 := make(chan bool) 301 | ch2 := make(chan bool) 302 | ch22 := make(chan bool) 303 | ch3 := make(chan bool) 304 | ch33 := make(chan bool) 305 | ch4 := make(chan bool) 306 | ch44 := make(chan bool) 307 | ch5 := make(chan bool) 308 | ch55 := make(chan bool) 309 | ch6 := make(chan bool) 310 | ch66 := make(chan bool) 311 | ch7 := make(chan bool) 312 | ch77 := make(chan bool) 313 | ch8 := make(chan bool) 314 | ch88 := make(chan bool) 315 | ch9 := make(chan bool) 316 | ch99 := make(chan bool) 317 | ch10 := make(chan bool) 318 | ch100 := make(chan bool) 319 | 320 | start_unix_time := time.Now().UnixNano() 321 | go func() { 322 | get_test_values("127.0.0.1:11000", false) 323 | ch1 <- true 324 | }() 325 | go func() { 326 | get_test_values("127.0.0.1:11000", true) 327 | ch11 <- true 328 | }() 329 | go func() { 330 | get_test_values("127.0.0.1:11001", false) 331 | ch2 <- true 332 | }() 333 | go func() { 334 | get_test_values("127.0.0.1:11001", true) 335 | ch22 <- true 336 | }() 337 | go func() { 338 | get_test_values("127.0.0.1:11002", false) 339 | ch3 <- true 340 | }() 341 | go func() { 342 | get_test_values("127.0.0.1:11002", true) 343 | ch33 <- true 344 | }() 345 | go func() { 346 | get_test_values("127.0.0.1:11003", false) 347 | ch4 <- true 348 | }() 349 | go func() { 350 | get_test_values("127.0.0.1:11003", true) 351 | ch44 <- true 352 | }() 353 | go func() { 354 | get_test_values("127.0.0.1:11004", false) 355 | ch5 <- true 356 | }() 357 | go func() { 358 | get_test_values("127.0.0.1:11004", true) 359 | ch55 <- true 360 | }() 361 | go func() { 362 | get_test_values("127.0.0.1:11005", false) 363 | ch6 <- true 364 | }() 365 | go func() { 366 | get_test_values("127.0.0.1:11005", true) 367 | ch66 <- true 368 | }() 369 | go func() { 370 | get_test_values("127.0.0.1:11006", false) 371 | ch7 <- true 372 | }() 373 | go func() { 374 | get_test_values("127.0.0.1:11006", true) 375 | ch77 <- true 376 | }() 377 | go func() { 378 | get_test_values("127.0.0.1:11007", false) 379 | ch8 <- true 380 | }() 381 | go func() { 382 | get_test_values("127.0.0.1:11007", true) 383 | ch88 <- true 384 | }() 385 | go func() { 386 | get_test_values("127.0.0.1:11008", false) 387 | ch9 <- true 388 | }() 389 | go func() { 390 | get_test_values("127.0.0.1:11008", true) 391 | ch99 <- true 392 | }() 393 | go func() { 394 | get_test_values("127.0.0.1:11009", false) 395 | ch10 <- true 396 | }() 397 | go func() { 398 | get_test_values("127.0.0.1:11009", true) 399 | ch100 <- true 400 | }() 401 | 402 | <-ch1 403 | <-ch11 404 | <-ch2 405 | <-ch22 406 | <-ch3 407 | <-ch33 408 | <-ch4 409 | <-ch44 410 | <-ch5 411 | <-ch55 412 | <-ch6 413 | <-ch66 414 | <-ch7 415 | <-ch77 416 | <-ch8 417 | <-ch88 418 | <-ch9 419 | <-ch99 420 | <-ch10 421 | <-ch100 422 | 423 | end_unitx_time := time.Now().UnixNano() 424 | time_to_query := (float64(end_unitx_time-start_unix_time) / float64(num)) / float64(1000) 425 | fmt.Printf("%f usec/query in parallel\n", time_to_query) 426 | } 427 | 428 | func main() { 429 | platform = runtime.GOOS 430 | 431 | op := flag.String("op", "setup-nodes", "setup chord network") 432 | arg1 := flag.String("arg1", "30", "argument if operation needs it") 433 | flag.Parse() 434 | 435 | switch *op { 436 | case "setup-nodes": 437 | node_num, _ := strconv.Atoi(*arg1) 438 | setup_nodes(node_num) 439 | break 440 | case "check-chain": 441 | check_chain_with_successor_info() 442 | break 443 | case "put-test-values": 444 | addr_and_port := *arg1 445 | put_test_values(addr_and_port) 446 | break 447 | case "get-test-values": 448 | addr_and_port := *arg1 449 | get_test_values(addr_and_port, false) 450 | break 451 | case "get-test-values-parallel": 452 | get_test_values_parallel() 453 | break 454 | default: 455 | fmt.Println("dkvs_client -op= -arg1=") 456 | } 457 | 458 | //test_get_request_which_has_query_string() 459 | //test_post_request_deserialize() 460 | //test_process_exec() 461 | //test_get_request_Result_type_return() 462 | //setup_nodes(40) 463 | //check_chain_with_successor_info() 464 | fmt.Println("finished!") 465 | } 466 | -------------------------------------------------------------------------------- /tools/rust_dkvs.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | @rem ..\target\debug\rust_dkvs.exe %1 %2 %3 %4 %5 %6 > stdout%1.txt 2>&1 3 | ..\target\release\rust_dkvs.exe %1 %2 %3 %4 %5 %6 > stdout%1.txt 2>&1 -------------------------------------------------------------------------------- /tools/rust_dkvs.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | #../target/debug/rust_dkvs $1 $2 $3 $4 $5 $6 > stdout$1.txt 2>&1 4 | ../target/release/rust_dkvs $1 $2 $3 $4 $5 $6 > stdout$1.txt 2>&1 --------------------------------------------------------------------------------