It seems like I found the reason. I've recreated the "content_idx" gin
(content jsonb_path_ops) with fastupdate=false, and good plan stayed even
on deadtuples = 4000+ ( as I said previously good plan changed to bad
starting on ~300 dead tuples in table).
So, as far as I understand the issue raises once gin pending-entry list
reaches ~300 entries. AFAIK gin pending-entry list is stored unsorted, so
optimizer may decide to choose another plan to be used since cost
estimations for scanning unsorted gin pending-entry list may be to high.
Once I disabled fastupdate mechanism I've raised overhead for write
operations, but scanning uses only index body, without gin pending-entry
(since it's not presented anymore).
Would much appreciate if someone can confirm or disprove my conclusions.