### Apache Airflow version

Other Airflow 2 version (please specify below)

### What happened

Our scheduler started failing with this trace:

  File "/usr/local/lib/python3.9/site-packages/airflow/models/taskinstance.py", line 1187, in get_failed_dep_statuses
    for dep_status in dep.get_dep_statuses(self, session, dep_context):
  File "/usr/local/lib/python3.9/site-packages/airflow/ti_deps/deps/base_ti_dep.py", line 95, in get_dep_statuses
    yield from self._get_dep_statuses(ti, session, dep_context)
  File "/usr/local/lib/python3.9/site-packages/airflow/ti_deps/deps/not_in_retry_period_dep.py", line 47, in _get_dep_statuses
    next_task_retry_date = ti.next_retry_datetime()
  File "/usr/local/lib/python3.9/site-packages/airflow/models/taskinstance.py", line 1243, in next_retry_datetime
    return self.end_date + delay
OverflowError: date value out of range

We found a dag with a large # of retries and exponential backoff will trigger this date error and take down the entire scheduler. The workaround is to force a max_delay setting. 

The bug is here:
https://github.com/apache/airflow/blob/2.3.3/airflow/models/taskinstance.py#L1243

The current version seems to use the same code:
https://github.com/apache/airflow/blob/main/airflow/models/taskinstance.py#L1147


### What you think should happen instead

There are a few solutions. Exponential backoff should probably require a max delay value.

At the very least, it shouldn't kill the scheduler.

### How to reproduce

Create dag with exponential delay and force it to retry until it overflows.

### Operating System

linux

### Versions of Apache Airflow Providers

_No response_

### Deployment

Astronomer

### Deployment details

_No response_

### Anything else

_No response_

### Are you willing to submit PR?

- [ ] Yes I am willing to submit a PR!

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)

)
            .all()
        )
        dag_run_state = DagRunState(dag_run_state)  
        for dr in drs:
            dr.state = dag_run_state
            dr.start_date = timezone.utcnow()
            if dag_run_state == DagRunState.QUEUED:
                dr.last_scheduling_decision = None
                dr.start_date = None
    session.flush()
def _is_mappable_value(value: Any) -> TypeGuard[Collection]:
    """Whether a value can be used for task mapping.
    We only allow collections with guaranteed ordering, but exclude character
    sequences since that's usually not what users would expect to be mappable.
    """
    if not isinstance(value, (collections.abc.Sequence, dict)):
        return False
    if isinstance(value, (bytearray, bytes, str)):
        return False
    return True
class TaskInstanceKey(NamedTuple):
    """Key used to identify task instance."""
    dag_id: str
    task_id: str
    run_id: str
    try_number: int = 1
    map_index: int = -1
    @property
    def primary(self) -> tuple[str, str, str, int]: