Design and implement a simple in-memory order book for a single stock that supports limit buy and limit sell orders. The book must maintain two price-time priority sides: bids (buy orders) sorted highest-price-first, and asks (sell orders) sorted lowest-price-first. When a new order arrives, attempt to match it immediately against the opposite side: a buy order trades with any ask whose price is ≤ the buy price, and a sell order trades with any bid whose price is ≥ the sell price. Matches consume liquidity at the best available prices until either the incoming order is fully filled or no more matches are possible; any remaining quantity is inserted into the book. The API must also support cancel(orderId) and two snapshot queries: getBestBid() → (price, size) and getBestAsk() → (price, size). All operations must be O(log n) or better.