본문으로 건너뛰기
Folly Code Review · 4/89

Folly API stability 정책 — 어떤 보장도 없다는 솔직함

· Hawk · 4분 읽기

한 줄 요약: Folly는 API/ABI 안정성을 약속하지 않는다. 이는 무책임이 아니라 fbcode monorepo의 운영 모델을 OSS에 그대로 노출한 결과다. 외부 사용자는 commit 단위 freeze로 이를 보완한다.

#동기 — 왜 보장을 하지 않는가

fbcode 안에서는 모든 호출자가 동시에 빌드된다. Folly 헤더를 바꾸면 그 시점의 trunk에 들어 있는 모든 호출자가 같이 컴파일된다. signature를 바꾸고 싶다면 호출자도 함께 바꾸면 된다. 언제든 가능하다.

OSS 사용자는 그 가정 밖에 있다. Meta는 OSS 사용자가 fbcode와 다른 schedule로 업데이트한다는 점을 안다. 그래서 다음 둘 중 하나를 택해야 한다.

  1. OSS 사용자를 위해 별도의 안정화 layer를 둔다.
  2. 안정성 약속을 하지 않고, 사용자가 lockstep으로 따라오게 한다.

Meta는 두 번째를 택했다. Folly는 그래서 다음을 보장하지 않는다.

  • API 호환성 (signature, default argument)
  • ABI 호환성 (struct layout, virtual table)
  • behaviour 호환성 (throw 여부, exception type)
  • header 위치 (folly/Foo.hfolly/bar/Foo.h로 이동 가능)
  • 이름 (folly::Promise::setExceptionsetError로 rename 가능)

#실제로 일어나는 변화

GitHub commit history를 보면 패턴이 보인다.

v2023.06.05.00 → v2024.05.06.00 사이 변화 (예시)
- folly::SmallLocks::MicroLock layout 변경 (ABI break)
- folly::futures::Future::onError → thenError로 deprecated
- folly::experimental::* → folly::로 승격
- AtomicHashMap의 일부 method signature 변경
- IOBuf::create_combined 추가, IOBuf::wrap_buffer signature 보강

대부분은 API 추가/개선이지만 ABI break도 드물지 않다. Header 위치 변경, namespace 이동도 분기마다 한두 건씩 일어난다.

#외부 사용자의 전략

#1. Tag로 고정

FetchContent_Declare(
folly
GIT_REPOSITORY https://github.com/facebook/folly.git
GIT_TAG v2024.11.04.00 # 특정 release
)

main branch는 언제든 깨질 수 있다. CI green이라 해도 다음 commit이 우리 코드를 깨지 않는다는 보장은 없다.

#2. 격리 layer

// my_async.h — 외부에 노출되는 인터페이스
namespace myapp {
class AsyncOp {
public:
std::future<int> Run(); // 표준 future로 반환
};
}
// my_async.cpp — 내부 구현
#include <folly/futures/Future.h>
std::future<int> AsyncOp::Run() {
folly::SemiFuture<int> sf = ...;
return std::async(std::launch::deferred, [sf = std::move(sf)]() mutable {
return std::move(sf).get();
});
}

Public header에서 Folly type을 노출하지 않으면 Folly 버전 업그레이드 시 cpp 파일만 영향받는다. 다운스트림은 무관하다.

#3. 단위 테스트로 회귀 감지

TEST(FollyContract, FutureThenValueReturnsValue) {
auto f = folly::makeFuture(42).thenValue([](int x) { return x + 1; });
EXPECT_EQ(std::move(f).get(), 43);
}

Folly의 behaviour를 우리 가정대로 동작하는지 검증하는 테스트를 둔다. 다음 버전에서 깨지면 즉시 알 수 있다.

#4. 큰 점프 회피

전략평가
v2022 → v2024 한 번에 업그레이드위험
v2022 → v2023 → v2024 단계적안전

분기마다 changelog가 따로 있지는 않지만, tag 사이의 diff는 작다. 작은 점프를 자주 하는 게 큰 점프 한 번보다 안전하다.

#ABI 측면

ABI break는 링크 시점에 들킨다.

undefined symbol: folly::AsyncSocket::AsyncSocket(folly::EventBase*, int, ...)

Folly와 application이 다른 시점의 헤더로 컴파일되면 발생한다. 해결책은 단 하나, 같은 commit으로 동시 빌드다.

# 정적 링크 + 단일 toolchain
set(BUILD_SHARED_LIBS OFF)
add_compile_options(-fvisibility=hidden -fvisibility-inlines-hidden)

Folly를 dynamic library로 배포하지 마라. 다른 application이 다른 시점의 Folly와 링크되면 undefined behaviour다.

#Meta의 입장 — Compatibility Document

folly/CMake/COMPATIBILITY.md(혹은 folly/docs/ 안)에 다음 취지의 문서가 있다.

Folly does not guarantee API or ABI stability between any two commits. External users are expected to update Folly together with their applications, treating Folly as part of their own source tree.

이는 Boost의 모델과는 정반대다. Boost는 release를 통한 안정성을 약속한다. Folly는 fbcode trunk를 약속한다.

#std/Abseil과의 비교

라이브러리API 안정성ABI 안정성버전 schedule
C++ 표준강력 (deprecation cycle)처리계별3년
AbseilLTS 안에서 약속동일 빌드 내LTS + main
Boostrelease 안에서 강력release 안에서4개월
Folly어떤 약속도 없음어떤 약속도 없음불규칙 tag

#코드 리뷰 포인트

  • main branch가 import되는가? PR에서 즉시 거절. tag로 고정한다.
  • public header에 Folly type이 있는가? SDK라면 격리한다.
  • Folly upgrade PR이 단독으로 올라왔는가? Folly만 올리지 말고 application code와 동시에 올린다.
  • 버전 점프가 1년 이상인가? 단계적 업그레이드를 고려한다.

#자주 보는 안티패턴

// 1. Public API에 folly::Future 노출
namespace mylib {
folly::SemiFuture<Result> ComputeAsync(); // 다운스트림이 Folly에 결박됨
}
// 2. Folly object를 dynamic library 경계로 전달
extern "C" folly::IOBuf* GetBuffer(); // ABI는 internal — 위험
# 3. main branch 사용
FetchContent_Declare(folly GIT_TAG main)
# 4. shared library로 배포
add_library(folly SHARED ...)
# 다른 application이 다른 commit의 folly와 충돌

#정리

  • Folly는 API/ABI 안정성을 약속하지 않는다. 이는 fbcode 운영 모델의 직접적 결과다.
  • 외부 사용자는 tag 고정 + 격리 layer + 회귀 테스트 + 단계적 업그레이드로 보완한다.
  • Folly를 dynamic library로 배포하지 말고 정적 링크로 application과 lockstep을 유지한다.
  • Public header에 Folly type을 노출하지 마라. SDK 호환성이 깨진다.
  • Abseil/Boost와는 완전히 다른 운영 가정 위에 있다.

#다음 편

Part 1-05: Production validation 문화에서 Meta production scale이 라이브러리 품질에 미치는 영향을 본다.

#관련 항목

Folly Code Review · 5 of 89

  1. 1 Folly Code Review — Meta의 production-grade C++ 라이브러리 코드 분석
  2. 2 Folly 개요 — Meta가 production에서 검증한 utility 모음 분석
  3. 3 Folly vs Abseil 철학 비교 — performance-first vs std-compatible
  4. 4 Folly 빌드와 fbcode 환경 — monorepo의 그림자
  5. 5 Folly API stability 정책 — 어떤 보장도 없다는 솔직함
  6. 6 Folly production validation 문화 — peta-scale에서 단련된 코드
  7. 7 folly::Future 분석 — std::future의 한계를 넘는 composable async
  8. 8 folly::Promise·makeFuture — Future를 만드는 두 길
  9. 9 folly::SemiFuture vs Future — executor binding의 명시화
  10. 10 folly::Future thenValue·thenError·thenTry — continuation 체인 분석
  11. 11 folly::collect·collectAll·collectAny — fan-in 패턴 분석
  12. 12 folly::Future retry·window·via — 제어 흐름 조합자
  13. 13 folly::fibers 분석 — M:N stackful coroutine
  14. 14 folly::InlineExecutor — 호출자 thread에서 즉시 실행
  15. 15 folly::CPUThreadPoolExecutor — CPU-bound 작업의 표준 thread pool
  16. 16 folly::IOThreadPoolExecutor — libevent 기반 I/O pool
  17. 17 folly::ManualExecutor — 결정적 테스트를 위한 수동 진행
  18. 18 folly::EventBase 분석 — libevent 이벤트 루프의 핵심
  19. 19 folly::IOBuf 분석 — zero-copy buffer chain의 기본 단위
  20. 20 folly::IOBufQueue — chain의 push/pull 추상화
  21. 21 folly::io::Cursor·RWCursor — chain 위의 stream
  22. 22 folly Zero-copy 패턴 — IOBuf로 ScatterGather I/O 표현
  23. 23 folly::IOBuf shared semantics — clone·unshare·takeOwnership
  24. 24 folly::FBString 분석 — SSO + COW 구현
  25. 25 folly의 fmt::format 통합 — 모던 포맷팅 채택
  26. 26 folly::StringPiece — string_view 호환 분석
  27. 27 folly Join·Split utilities — 문자열 분해와 결합
  28. 28 folly::to·tryTo — text↔num 변환 분석
  29. 29 folly Conv Customization — 사용자 타입 지원
  30. 30 folly Conv 성능 비교 — sprintf·stringstream 대비
  31. 31 folly::F14ValueMap vs std::unordered_map
  32. 32 folly::F14NodeMap — stable pointer가 필요할 때
  33. 33 folly::F14VectorMap — cache-friendly iteration
  34. 34 folly::F14FastMap — auto-select 동작
  35. 35 folly F14 internals — SIMD probing 메커니즘
  36. 36 folly::small_vector — inline storage 분석
  37. 37 folly::FixedString — compile-time string
  38. 38 folly::AtomicHashMap — lock-free read 분석
  39. 39 folly::ConcurrentHashMap — sharded 동시 해시 맵
  40. 40 folly::EvictingCacheMap — LRU 구현 분석
  41. 41 folly::Synchronized — lock wrapper 패턴
  42. 42 folly::SharedMutex 분석
  43. 43 folly::Baton — one-shot wait 동기화
  44. 44 folly::RWSpinLock 분석
  45. 45 folly::PicoSpinLock — 1-byte spinlock
  46. 46 folly::ProducerConsumerQueue — SPSC 큐 분석
  47. 47 folly::MPMCQueue — multi-producer multi-consumer
  48. 48 folly::UnboundedQueue — 동적 크기 lock-free
  49. 49 folly::fibers::Channel — Go-like channel
  50. 50 folly::dynamic — JSON-like dynamic type 분석
  51. 51 folly JSON conversion — toJson·parseJson
  52. 52 folly dynamic ↔ struct — manual marshaling
  53. 53 folly dynamic Visitor pattern — type별 분기
  54. 54 folly::Singleton vs Meyers/static — 왜 Folly의 Singleton인가
  55. 55 folly::SingletonVault 분석 — 등록·소멸·의존성
  56. 56 folly::Singleton try_get·try_get_fast — TLS-cached 접근
  57. 57 folly::ExceptionWrapper — type-erased exception holder
  58. 58 folly::ScopeGuard·SCOPE_EXIT — RAII cleanup
  59. 59 folly::Optional vs std::optional
  60. 60 folly::Function vs std::function
  61. 61 folly::Lazy — 지연 초기화 wrapper
  62. 62 folly Meta 스타일 code review 패턴
  63. 63 folly anti-patterns — 잘못 쓰면 std보다 느림
  64. 64 folly vs std 선택 기준 분석
  65. 65 folly::coro 개요 — production C++20 코루틴 어댑터
  66. 66 folly::coro::Task — lazy single-shot 코루틴
  67. 67 folly::coro::AsyncGenerator — 비동기 스트림
  68. 68 folly coro blockingWait·collectAll — 동기 경계와 fan-in
  69. 69 folly::coro::Baton·Mutex — 코루틴-aware 동기화
  70. 70 folly::Expected — 결과 또는 오류
  71. 71 folly::Try — Future 결과 wrapper
  72. 72 folly::Try vs Expected 선택 기준
  73. 73 folly::Range — 일반 iterator pair
  74. 74 folly::Uri — URL 파서
  75. 75 folly Fingerprint64·128 — 분산 hash
  76. 76 folly SpookyHashV2 — fast non-crypto hash
  77. 77 folly::Init — main() 부트스트랩
  78. 78 folly::Indestructible — global lifetime 패턴
  79. 79 folly::MicroLock — 1-byte 락
  80. 80 folly::MicroSpinLock — 가장 좁은 spin lock
  81. 81 folly::format — legacy formatter 분석
  82. 82 folly::demangle — typeid 디망글링
  83. 83 folly::DynamicConverter — dynamic ↔ struct
  84. 84 folly::RecordIO — append-only 로그 파일 포맷
  85. 85 folly::io::Compression — zstd·lz4·snappy wrapper
  86. 86 folly::AsyncIO — io_uring·Linux AIO
  87. 87 folly::CancellationToken — 코루틴·Future 취소 전파
  88. 88 folly::observer — hot config의 atomic refresh
  89. 89 fbcode 패턴 모음 — folly 사용의 실전