Не удается получить доступ к удаленному объекту в функции, возвращает список? - PullRequest
0 голосов
/ 31 августа 2018

У меня есть следующая функция, чтобы вывести список всех объектов в папке S3.

let listObjects bucketName prefix =
    try
        use client = new AmazonS3Client(RegionEndpoint.USEast2)
        let request = new ListObjectsV2Request(BucketName = bucketName, MaxKeys = 1000, Prefix = prefix)

        let rec getKeys() = asyncSeq {
            let! response = client.ListObjectsV2Async(request) |> Async.AwaitTask // Error
            for entry in response.S3Objects do
                yield entry.Key
            if response.IsTruncated then yield! getKeys()
            }

        getKeys() |> AsyncSeq.toList
    with
    | :? AmazonS3Exception as s3ex ->
        Log.Error("S3 error occurred. Exception: {0}", s3ex)
        List.empty
    | ex ->
        Log.Error("Exception: {0}\n{1}", ex.Message, ex)
        List.empty

Тем не менее, он получил ошибку в строке let! response = client.ListObjectsV2Async(request) |> Async.AwaitTask.

Необработанное исключение: System.ObjectDisposedException: Невозможно получить доступ к удаленному объекту. Имя объекта: «Amazon.S3.AmazonS3Client». в Amazon.Runtime.AmazonServiceClient.ThrowIfDisposed () at Amazon.Runtime.AmazonServiceClient.InvokeAsync [TRequest, TResponse] (запрос TRequest, маршаллер IMarshaller`2, демаршаллер ResponseUnmarshaller, CancellationToken cancellationToken) на Amazon.S3.AmazonS3Client.ListObjectsV2Async (ListObjectsV2Request запрос, CancellationToken cancellationToken)


Следующий код, вызывающий Dispose(), явно работает.

let listObjects bucketName prefix =
    let client = new AmazonS3Client(RegionEndpoint.USEast2)
    try
        try
            let request = new ListObjectsV2Request(BucketName = bucketName, MaxKeys = 1000, Prefix = prefix)
            let rec getKeys() = asyncSeq {
                let! response = client.ListObjectsV2Async(request) |> Async.AwaitTask
                for entry in response.S3Objects do
                    yield entry.Key
                if response.IsTruncated then yield! getKeys()
                }
            getKeys() |> AsyncSeq.toList
        with
        | :? AmazonS3Exception as s3ex ->
            Log.Error("S3 error occurred. Exception: {0}", s3ex)
            List.empty
        | ex ->
            Log.Error("Exception: {0}\n{1}", ex.Message, ex)
            List.empty
    finally
        client.Dispose()

1 Ответ

0 голосов
/ 31 августа 2018

Это не ответ на ваш вопрос, но я думаю, что то, как вы комбинируете асинхронные последовательности с синхронным кодом, на самом деле не дает вам никаких преимуществ. Асинхронные последовательности полезны, если вы хотите написать код, который будет перебирать данные, поступающие из какого-либо асинхронного источника данных, без блокировки, но если вы просто преобразуете свою последовательность в список, то нет смысла использовать асинхронные последовательности.

Лучшим подходом, который мог бы фактически решить вашу проблему, было бы изменение функции listObjects, чтобы она сама возвращала асинхронную последовательность. Затем вы можете использовать конструкцию use внутри асинхронной последовательности, а вычислительное выражение должно позаботиться об удалении объекта только после полной итерации последовательности:

let listObjects bucketName prefix = asyncSeq {
    try
        use client = new AmazonS3Client(RegionEndpoint.USEast2)
        let request = 
            new ListObjectsV2Request(BucketName = bucketName, MaxKeys = 1000, Prefix = prefix)
        let rec getKeys() = asyncSeq {
            let! response = client.ListObjectsV2Async(request) |> Async.AwaitTask 
            for entry in response.S3Objects do
                yield entry.Key
            if response.IsTruncated then yield! getKeys() }

        yield! getKeys()
    with
    | :? AmazonS3Exception as s3ex ->
        Log.Error("S3 error occurred. Exception: {0}", s3ex)
    | ex ->
        Log.Error("Exception: {0}\n{1}", ex.Message, ex)  }

Если вам действительно нужно сделать вещи синхронными, вы можете затем вызвать AsyncSeq.toList по результату функции:

listObjects "whatever" "prefix" |> AsyncSeq.toList

Тем не менее, я не уверен, что это действительно решит вашу проблему. Я думаю, что ваш код должен работать как есть, и я не совсем уверен, как поведение может быть вызвано ошибкой в ​​асинхронных последовательностях. Поэтому я предполагаю, что в вашем коде есть что-то еще, что вызывает ошибку.

РЕДАКТИРОВАТЬ Я только что попытался воспроизвести это поведение без всех ссылок, которые требуются в вашем коде, поэтому я адаптировал его, чтобы просто перебирать списки и печатать при удалении объекта. Вот мой код:

let listObjects () =
  try
    use client = { new IDisposable with member x.Dispose() = printfn "bye" }
    let rec getKeys n = asyncSeq {
        let! nn = Async.Sleep(1)
        for i in 0 .. 1 do yield i
        if n <> 0 then yield! getKeys(n-1) 
        else printfn "completed" }
    printfn "getting"
    getKeys 5 |> AsyncSeq.toList
  with
  | ex ->
      printfn "nop"
      List.empty

listObjects ()

Это правильно делает - печатает «получение», «завершение», а затем «пока», поэтому, если я случайно не изменил что-то существенное, похоже, что asyncSeq здесь все делает правильно.

Можете ли вы попробовать добавить похожие use и printf в ваш код, просто чтобы убедиться, что в вашем случае поведение такое же?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...