Is it bad practice to do additional work in IAsyncEnumerable generator method?

Imagine you have to process each row in large table.
For every single row you have to download some data from a web service and store it in a different database.

InfrastructeOverview

Loading all rows at once into the ConsoleApp's memory consumes too much resources and takes too long.

It's important to mention that the table will no longer modified, only Select statements are possible and the data can be queried ordered by the primary key which is an integer value.

A possible solution is to load one row after another.
For doing so you need two things.
Firstly, the table must be queryable in an ordered manner (unique index).
Secondly, you need to know the last loaded row to get the next one.

A simple example using IAsyncEnumerable could look like the following.

  async IAsyncEnumerable<Data> GetRowsAsync()
  {
    // primary key starts at 1
    var lastId = 0;
    while(true)
    {
      Data next = await LoadNextRowAsync(lastId); // where id is greater than lastId

      if(next is null)
      {
        break;
      }

      yield return next;

      lastId = next.Id
    }
  }

But making a network call for each row creates an overhead that makes the app feel like it never ends.

The first way that comes in mind to avoid that overhead is to load n rows rather than 1 row.

  async IAsyncEnumerable<Data> GetRowsAsync()
  {
    var lastId = 0;
    while(true)
    {
      List<Data> nextRows = await LoadNextRowsAsync(lastId: lastId, nElements: 500);

      if(!nextRows.Any())
      {
        break;
      }

      yield return nextRows;

      lastId = nextRows.Last().Id
    }
  }

Great, this makes the app much faster.
But it still takes a while till all rows have been processed.

After some time the app crashed due to a bug and the last id got lost.
So I have to start from scratch again after fixing the bug and I can't garantuee that there are no more bugs.
This leads to a new requirement.

I want to be able to stop processing rows and continue later without starting from the beginnging.

The easiest way to achieve this is by persisting the lastId, e.g. in a file located in the output directory of the app.

  async IAsyncEnumerable<Data> GetRowsAsync()
  {
    while(true)
    {
      // load last id from file in out directory
      var lastId = await GetLastIdOrDefaultAsync();

      List<Data> nextRows = await LoadNextRowsAsync(lastId: lastId, nElements: 500);

      if(!nextRows.Any())
      {
        break;
      }

      yield return nextRows;

      // save last id in file
      await UpdateLastIdAsync(nextRows.Last().Id);
    }
  }

This works fine and fulfils all requirements.
Now to my actual question.

Do I abuse IAsyncEnumerable in my final solution?

In almost all examples I've seen IAsyncEnumerable is used in much simpler ways and I wonder if it is bad practice to do that additional IO for storing ids inside the generator method.

Is it bad practice to do additional work in IAsyncEnumerable generator method?