This is a work-in-progress that certainly needs more testing and cleanup -- sharing now in light of discussions happening elsewhere (#6972) about some of the drawbacks from introducing mempool limiting in #6722.
Overall I agree with @sturles in that the existing notion of priority is largely a useful one to differentiate transactions that might be part of an attack from ones that likely are not. On the other hand, coming up with a mempool limiting algorithm like #6722 that we believe is robust against attack was challenging enough without having to also try to maintain this particular existing policy.
However after further thought I think we can restore some of the useful features of priority without major re-engineering of the mempool. I propose to reserve some fraction of the mempool (defaults to the blockprioritysize/blockmaxsize) for transactions that have no unconfirmed parents. Transactions in that space will be sorted by priority at the time of entry to the mempool.
If the priority space in the mempool exceeds its limit, priority transactions will be moved to the feerate portion of the mempool (where they will be sorted based on their feerate and the feerate of any descendants). Once moved to the feerate area, transactions never move back to the priority area.
New transactions that don't meet the mempool reject fee must meet the new GetMinPriority(), which is increased when priority transactions are moved to the feerate part of the mempool, and decayed as time passes (just like the mempool's minimum fee).
Note that this relies on the rate limiting of free transactions to prevent relay bandwidth DoS attacks using priority transactions.