Raft 기반 시스템에서 올바른 HTTP 메서드 선택

Tasker_Jang·2024년 10월 5일
0

1. PUT 메서드: /store/{key}

  • 기능: 주어진 key에 해당하는 값을 저장하거나 업데이트.
  • 현재 상태: PUT /store/{key}/{value}로 잘못된 RESTful 설계를 사용하고 있음.
  • 적절한 수정: value는 요청 본문(body)에 포함하고, key만 URL 경로에 포함시켜야 함.
#[put("/store/{key}")]
async fn put(
    data: web::Data<(HashStore, Raft)>, 
    path: web::Path<u64>, 
    body: web::Json<String>
) -> impl Responder {
    let key = path.into_inner();
    let value = body.into_inner();
    let log_entry = LogEntry::Insert { key, value };

    match log_entry.encode() {
        Ok(encoded_entry) => {
            if let Err(e) = data.1.propose(encoded_entry).await {
                return HttpResponse::InternalServerError().body(format!("Failed to propose: {}", e));
            }
            HttpResponse::Ok().body("OK")
        }
        Err(e) => HttpResponse::BadRequest().body(format!("Encoding error: {}", e)),
    }
}

2. GET 메서드: /store/{key}

  • 기능: 주어진 key에 해당하는 값을 조회.
  • 적절성: 적절함. 데이터 조회 작업이므로 GET이 맞음.
  • 개선 사항: 리소스가 없을 때 404 Not Found 반환.
#[get("/store/{key}")]
async fn get(data: web::Data<(HashStore, Raft)>, path: web::Path<u64>) -> impl Responder {
    let key = path.into_inner();

    match data.0.get(key) {
        Some(value) => HttpResponse::Ok().body(value),
        None => HttpResponse::NotFound().body("Item not found"),
    }
}

3. GET 메서드: /leader

  • 기능: 현재 리더 노드의 ID를 조회.
  • 적절성: 적절함. 조회 작업이므로 GET 메서드 사용이 맞음.
  • 개선 사항: 에러 처리 추가.
#[get("/leader")]
async fn leader(data: web::Data<(HashStore, Raft)>) -> impl Responder {
    match data.1.get_leader_id().await {
        Ok(leader_id) => HttpResponse::Ok().body(leader_id.to_string()),
        Err(e) => HttpResponse::InternalServerError().body(format!("Failed to get leader id: {}", e)),
    }
}

4. POST 메서드: /leave

  • 기능: 노드가 Raft 클러스터에서 탈퇴.
  • 현재 상태: GET /leave로 잘못 설계됨.
  • 적절한 수정: 상태 변경 작업이므로 POST 사용.
#[post("/leave")]
async fn leave(data: web::Data<(HashStore, Raft)>) -> impl Responder {
    if let Err(e) = data.1.leave().await {
        return HttpResponse::InternalServerError().body(format!("Failed to leave cluster: {}", e));
    }
    HttpResponse::Ok().body("OK")
}

5. GET 메서드: /debug

  • 기능: Raft 시스템의 디버깅 정보를 조회.
  • 적절성: 적절함. 상태를 변경하지 않으므로 GET 메서드 사용이 맞음.
  • 개선 사항: 에러 처리 추가 및 JSON 응답 처리.
#[get("/debug")]
async fn debug(data: web::Data<(HashStore, Raft)>) -> impl Responder {
    match data.1.inspect().await {
        Ok(json) => match serde_json::from_str::<HashMap<String, Value>>(&json) {
            Ok(parsed) => HttpResponse::Ok().json(parsed),
            Err(e) => HttpResponse::InternalServerError().body(format!("Failed to parse JSON: {}", e)),
        }
        Err(e) => HttpResponse::InternalServerError().body(format!("Failed to inspect raft: {}", e)),
    }
}

6. POST 메서드: /snapshot

  • 기능: Raft 스냅샷 생성.
  • 현재 상태: GET /snapshot으로 잘못 설계됨.
  • 적절한 수정: 서버 상태를 변경하는 작업이므로 POST 사용.
#[post("/snapshot")]
async fn snapshot(data: web::Data<(HashStore, Raft)>) -> impl Responder {
    match data.1.storage().await {
        Ok(storage) => {
            let last_index = storage.last_index().expect("Failed to get last index");
            let hard_state = storage.hard_state().expect("Failed to get hard state");

            match data.1.make_snapshot(last_index, hard_state.term).await {
                Ok(_) => HttpResponse::Ok().body("Snapshot created successfully"),
                Err(e) => HttpResponse::InternalServerError().body(format!("Failed to make snapshot: {}", e)),
            }
        }
        Err(e) => HttpResponse::InternalServerError().body(format!("Failed to get storage: {}", e)),
    }
}

7. GET 메서드: /peers

  • 기능: 클러스터의 피어 목록 조회.
  • 적절성: 적절함. 조회 작업이므로 GET 메서드 사용이 맞음.
  • 개선 사항: JSON 형식으로 응답.
#[get("/peers")]
async fn peers(data: web::Data<(HashStore, Raft)>) -> impl Responder {
    match data.1.get_peers().await {
        Ok(peers) => HttpResponse::Ok().json(peers),
        Err(e) => HttpResponse::InternalServerError().body(format!("Failed to get peers: {}", e)),
    }
}

8. POST 메서드: /leave_joint

  • 기능: Raft 공동 리더십(joint consensus)에서 탈퇴.
  • 현재 상태: GET /leave_joint로 잘못 설계됨.
  • 적절한 수정: 상태 변경 작업이므로 POST 사용.
#[post("/leave_joint")]
async fn leave_joint(data: web::Data<(HashStore, Raft)>) -> impl Responder {
    match data.1.leave_joint().await {
        Ok(_) => HttpResponse::Ok().body("Node has left joint consensus"),
        Err(e) => HttpResponse::InternalServerError().body(format!("Failed to leave joint consensus: {}", e)),
    }
}

9. POST 메서드: /transfer_leader/{id}

  • 기능: 리더십을 다른 노드로 이전.
  • 현재 상태: GET /transfer_leader/{id}로 잘못 설계됨.
  • 적절한 수정: 리더십 전환은 상태 변경 작업이므로 POST 사용.
#[post("/transfer_leader/{id}")]
async fn transfer_leader(data: web::Data<(HashStore, Raft)>, path: web::Path<u64>) -> impl Responder {
    let node_id: u64 = path.into_inner();

    match data.1.transfer_leader(node_id).await {
        Ok(_) => HttpResponse::Ok().body("Leader transferred successfully"),
        Err(e) => HttpResponse::InternalServerError().body(format!("Failed to transfer leader: {}", e)),
    }
}

10. POST 메서드: /campaign

  • 기능: Raft 리더 선출 캠페인 시작.
  • 현재 상태: GET /campaign으로 잘못 설계됨.
  • 적절한 수정: 상태 변경 작업이므로 POST 사용.
#[post("/campaign")]
async fn campaign(data: web::Data<(HashStore, Raft)>) -> impl Responder {
    match data.1.campaign().await {
        Ok(_) => HttpResponse::Ok().body("Campaign started successfully"),
        Err(e) => HttpResponse::InternalServerError().body(format!("Failed to start campaign: {}", e)),
    }
}

11. POST 메서드: /demote/{term}/{leader_id}

  • 기능: 리더 강등.

  • 현재 상태: GET /demote/{term}/{leader_id}로 잘못 설계됨.

  • 적절한 수정: 리더를 강등하는 상태 변경 작업이므로 POST

    사용.

#[post("/demote/{term}/{leader_id}")]
async fn demote(data: web::Data<(HashStore, Raft)>, path: web::Path<(u64, u64)>) -> impl Responder {
    let (term, leader_id) = path.into_inner();

    match data.1.demote(term, leader_id).await {
        Ok(_) => HttpResponse::Ok().body("Leader demoted successfully"),
        Err(e) => HttpResponse::InternalServerError().body(format!("Failed to demote leader: {}", e)),
    }
}

요약

  • GET 메서드읽기 전용 작업에 적합하며, 서버의 상태를 변경하지 않는 경우 사용됩니다.
  • POST 메서드서버 상태를 변경하거나 리소스를 생성할 때 사용됩니다.
  • 적절한 에러 처리JSON 형식 응답을 통해 API의 안정성과 사용성을 향상시킬 수 있습니다.
profile
터널을 지나고 있을 뿐, 길은 여전히 열려 있다.

0개의 댓글