Skip to content

Commit

Permalink
Merge pull request #1647 from ardriveapp/dev
Browse files Browse the repository at this point in the history
PE-5761: Release ArDrive App v2.37.0
  • Loading branch information
thiagocarvalhodev authored Mar 1, 2024
2 parents 11b2e93 + c518c90 commit 5eafead
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 34 deletions.
2 changes: 2 additions & 0 deletions android/fastlane/metadata/android/en-US/changelogs/112.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Enhances user experience and efficiency of Turbo uploads
- Fixes issue with upload folders using AR
5 changes: 4 additions & 1 deletion lib/components/upload_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -822,7 +822,10 @@ class _UploadFormState extends State<UploadForm> {
}

return ArDriveStandardModal(
width: kLargeDialogWidth,
hasCloseButton: true,
width: state.failedTasks != null
? kLargeDialogWidth
: kMediumDialogWidth,
title: 'Problem with Upload',
description: appLocalizationsOf(context).yourUploadFailed,
content: state.failedTasks != null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import 'package:ardrive_uploader/ardrive_uploader.dart';
import 'package:ardrive_uploader/src/exceptions.dart';
import 'package:ardrive_uploader/src/streamed_upload.dart';
import 'package:ardrive_uploader/src/turbo_upload_service.dart';
import 'package:arweave/arweave.dart';
import 'package:ardrive_uploader/src/utils/logger.dart';
import 'package:arweave/arweave.dart';
import 'package:flutter/foundation.dart';

class TurboStreamedUpload implements StreamedUpload<UploadItem> {
Expand Down
102 changes: 72 additions & 30 deletions packages/ardrive_uploader/lib/src/turbo_upload_service.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:math';
import 'dart:typed_data';

import 'package:ardrive_uploader/src/exceptions.dart';
Expand All @@ -17,7 +18,6 @@ class TurboUploadService {
final r = RetryOptions(maxAttempts: 8);
final List<CancelToken> _cancelTokens = [];
final dio = Dio();
final dataItemConfirmationRetryDelay = Duration(seconds: 15);
final maxInFlightData = MiB(100).size;
Timer? onSendProgressTimer;

Expand Down Expand Up @@ -138,20 +138,23 @@ class TurboUploadService {

final finaliseInfo = await r.retry(
() => dio.post(
'$turboUploadUri/chunks/arweave/$uploadId/-1',
'$turboUploadUri/chunks/arweave/$uploadId/finalize',
data: null,
cancelToken: finalizeCancelToken,
options: Options(
validateStatus: (int? status) {
return status != null &&
((status >= 200 && status < 300) || status == 504);
},
),
),
);

if (finaliseInfo.statusCode == 504) {
final confirmInfo = await _confirmUpload(dataItem.id);
if (finaliseInfo.statusCode == 202) {
// TODO: Send this upload to a queue. We'd need to change the
// type of the returned data though. Perhaps the returned object
// could be an event emitter that the calling client can use to
// listen for async outcomes like finalization success/failure.
final confirmInfo = await _confirmUpload(
dataItemId: dataItem.id,
uploadId: uploadId,
dataItemSize: dataItem.dataItemSize,
);

onSendProgressTimer?.cancel();

return confirmInfo;
Expand All @@ -176,32 +179,56 @@ class TurboUploadService {
}
}

Future<Response> _confirmUpload(TxID dataItemId) async {
try {
logger.d('[$dataItemId] Confirming upload to Turbo');
// TODO: This funciton as designed should go away, but some incremental
// improvements that could be helpful:
// - Don't use recursion. Use a while loop instead.
// - Have a max retry count and/or max finalization time
// - Make retries based on an exponential backoff rather than a fixed delay
// - Make starting time for first wait based on some linear function of file size
// - Don't keep retrying infinitely in the case of errors.
Future<Response> _confirmUpload({
required TxID dataItemId,
required String uploadId,
required int dataItemSize,
}) async {
final fileSizeInGiB = (dataItemSize.toDouble() / GiB(1).size).ceil();
final maxWaitTime = Duration(minutes: fileSizeInGiB);
logger.d(
'[$dataItemId] Confirming upload to Turbo with uploadId $uploadId for up to ${maxWaitTime.inMinutes} minutes.');

final startTime = DateTime.now();
final cutoffTime = startTime.add(maxWaitTime);
int attemptCount = 0;

while (DateTime.now().isBefore(cutoffTime)) {
final response = await dio.get(
'$turboUploadUri/v1/tx/$dataItemId/status',
'$turboUploadUri/chunks/arweave/$uploadId/status',
);

final responseData = response.data;

if (responseData['status'] == 'CONFIRMED' ||
responseData['status'] == 'FINALIZED') {
logger.d('[$dataItemId] DataItem confirmed!');
return response;
} else {
logger.d(
'[$dataItemId] DataItem not confirmed. Retrying in ${dataItemConfirmationRetryDelay.toString()}');

await Future.delayed(dataItemConfirmationRetryDelay);

return _confirmUpload(dataItemId);
final responseStatus = responseData['status'];
switch (responseStatus) {
case 'FINALIZED':
logger.d('[$dataItemId] DataItem confirmed!');
return response;
case 'UNDERFUNDED':
throw UploadCanceledException('Upload canceled. Underfunded.');
case 'ASSEMBLING':
case 'VALIDATING':
case 'FINALIZING':
final retryAfterDuration =
dataItemConfirmationRetryDelay(attemptCount++);
logger.d(
'[$dataItemId] DataItem not confirmed. Retrying in ${retryAfterDuration.inMilliseconds}ms');

await Future.delayed(retryAfterDuration);
default:
throw UploadCanceledException(
'Upload canceled. Finalization failed. Status: ${responseData['status']}');
}
} catch (e) {
await Future.delayed(dataItemConfirmationRetryDelay);

return _confirmUpload(dataItemId);
}
throw UploadCanceledException(
'Upload canceled. Finalization took too long.');
}

Future<void> cancel() {
Expand Down Expand Up @@ -299,3 +326,18 @@ Stream<Uint8List> streamToChunks(
yield buffer.toBytes();
}
}

final defaultBaseDuration = Duration(milliseconds: 250);

Duration dataItemConfirmationRetryDelay(
int iteration, {
Duration baseDuration = const Duration(milliseconds: 100),
Duration maxDuration = const Duration(seconds: 8),
}) {
return Duration(
milliseconds: min(
baseDuration.inMilliseconds * pow(2, iteration).toInt(),
maxDuration.inMilliseconds,
),
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Future<List<DataItemResult>> createDataItemResultFromDataItemFiles(
Wallet wallet,
) async {
final List<DataItemResult> dataItemList = [];
final dataItemCount = 2;
final dataItemCount = dataItemList.length;
for (var i = 0; i < dataItemCount; i++) {
final dataItem = dataItems[i];
await createDataItemTaskEither(
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: Secure, permanent storage

publish_to: 'none'

version: 2.36.0
version: 2.37.0

environment:
sdk: '>=3.0.2 <4.0.0'
Expand Down

0 comments on commit 5eafead

Please sign in to comment.