본문으로 건너뛰기

Plugin Pipeline — 외부 백엔드 연결 메커니즘

[!tldr] 업무 관점 takeaway plugin 타입이 FDP/HC-SSD 백엔드의 자연스러운 진입점이다. LMCache 코드를 수정하지 않고 L2AdapterInterface 구현체를 외부 모듈로 등록할 수 있다. config JSON에 "type": "plugin", module_path, class_name만 지정하면 된다. FDP placement 파라미터는 adapter_params로 전달.


계층 관계

L2AdapterInterface (추상)

├─ 빌트인 어댑터 (raw_block, dax, nixl, fs ...)
│ 직접 L2AdapterInterface 구현

└─ plugin / native_plugin ← 외부 코드를 연결하는 브리지

├─ "plugin" 외부에서 L2AdapterInterface 구현체를 통째로 로드
└─ "native_plugin" 외부에서 connector 6메서드 로드 → NativeConnectorL2Adapter 감쌈

plugin 타입

Config JSON

{
"type": "plugin",
"module_path": "lmc_fdp_backend.adapter",
"class_name": "FDPBackendL2Adapter",
"adapter_params": {
"device": "/dev/ng0n1",
"fdp_nruh": 8,
"placement_strategy": "by_cache_salt"
}
}
필드필수설명
module_path필수Python 모듈 dotted 경로
class_name필수L2AdapterInterface 구현 클래스명
adapter_params선택생성자에 전달할 kwargs
config_class_name선택config 클래스명 (없으면 자동 탐색)

플러그인이 반드시 해야 하는 것

  • L2AdapterInterface 서브클래스
  • 추상 메서드 전부 구현 (eventfd 3개 포함)
  • thread-safe 구현 (StoreController + PrefetchController 동시 호출)
  • **kwargs 수용 (forward-compatibility)

플러그인이 직접 해야 하는 것 (framework 미제공)

  • 자체 asyncio event loop + background thread 생성
  • create_event_notifier() (lmcache.v1.platform)로 eventfd 생성
  • close()에서 모든 리소스 정리

생성 흐름

_create_plugin_adapter(config, l1_memory_desc)

├─ 1. importlib.import_module(config.module_path)
├─ 2. getattr(module, config.class_name)
├─ 3. issubclass(adapter_cls, L2AdapterInterface) 확인
├─ 4. config class 자동 탐색 (우선순위 4단계)
│ 1순위: JSON의 config_class_name
│ 2순위: class_name + "Config" 컨벤션 (MyL2Adapter → MyL2AdapterConfig)
│ 3순위: adapter_cls.config_class_name 클래스 속성
│ 4순위: None (raw dict 그대로)
└─ 5a. [config class 발견] adapter_cls(cfg_cls.from_dict(adapter_params))
5b. [없음] adapter_cls(adapter_params)

native_plugin 타입 (C++/Rust 백엔드)

구분pluginnative_plugin
외부가 구현하는 것L2AdapterInterface 전체connector 6메서드
브리지 로직플러그인이 직접NativeConnectorL2Adapter 재사용
적합한 경우Python 플러그인C++/Rust pybind11 connector

필수 connector 메서드:

def event_fd(self) -> int: ...
def submit_batch_get(self, keys, memoryviews) -> int: ...
def submit_batch_set(self, keys, memoryviews) -> int: ...
def submit_batch_exists(self, keys) -> int: ...
def drain_completions(self) -> list[tuple[int, bool, str, list[bool] | None]]: ...
def close(self) -> None: ...

런타임 threading 모델 (plugin 내부)

Plugin.__init__()
├─ self._store_efd = create_event_notifier()
├─ self._lookup_efd = create_event_notifier()
├─ self._load_efd = create_event_notifier()
├─ self._loop = asyncio.new_event_loop()
└─ self._thread = Thread(target=run_loop, daemon=True).start()

StoreController 스레드 → submit_store_task()
└─ run_coroutine_threadsafe(coro, self._loop)

PrefetchController 스레드 → submit_load_task()
└─ run_coroutine_threadsafe(coro, self._loop)

Plugin background thread (event loop)
├─ coroutine 실행 후 eventfd 시그널
└─ 공유 상태 접근 시 lock 필요

framework가 event loop를 제공하지 않는다 — plugin이 직접 만들어야 한다.


FDP Backend 설계 시 주의사항

  • _notify_keys_stored(keys, sizes) — base class처럼 호출해야 eviction 회계가 작동함. super().__init__(max_capacity_bytes=...) 필수.
  • eventfd 3개는 전역 고유 필수. create_event_notifier()를 인스턴스별로 3번 호출.
  • HC-SSD offload의 경우 serde_wrapper.pySerdeL2AdapterWrapper 위치가 후보.

관련 페이지

  • [[L2-어댑터]] — L2AdapterInterface 전체 계약과 흐름
  • [[raw_block-io_uring-cmd]] — io_uring_cmd passthrough (FDP dspec 전달 경로)
  • [[NVMe-FDP]] — FDP placement handle, RUH