AWS SNS 取消订阅循环不释放内存 - 导致 Jetsam 终止应用程序
AWS SNS Unsubscribe loop is not releasing memory - Causing Jetsam to kill app
问题
当我取消订阅我的端点订阅时,仪器内存图从 32MB 变为 300MB 并且应用程序被杀死 Jetsam。据我了解,AWS BFTasks 会自行清理……如果我删除 do...while
并允许它处理前 100 个订阅,一切正常。
进程
我的开发应用程序中有大约 8000 个订阅,这些应用程序使用 AWS SNS 将警报推送到 iOS 设备和电子邮件。在用户选择他们想要的订阅后,我删除他们现有的订阅,然后订阅新的订阅。 [_awsSnsClient listSubscriptions:request]
returns 最多 100 个订阅,我遍历这些,根据需要执行 [_awsSnsClient unsubscribe:unsubscribeRequest]
。然后获得下一个 100...
代码
- (void) awsUsubscribeAllSubscriptions {
DLog(@"Removing existing subscriptions");
AWSSNSListSubscriptionsInput *request = [AWSSNSListSubscriptionsInput new];
__block Boolean sentLastUnsubscribe = NO; // YES when the last unsubscribe request was sent
__block int pendingUnsubscribe = 0; // Number of outsianding unsubscribes (async process not yet complete)
do {
[[[_awsSnsClient listSubscriptions:request] continueWithSuccessBlock:^id(BFTask *task) {
AWSSNSListSubscriptionsResponse *response = (AWSSNSListSubscriptionsResponse *)task.result;
NSString *nextToken = response.nextToken;
NSArray *subscriptions = response.subscriptions;
for (AWSSNSSubscription *subscription in subscriptions) {
if ([subscription.endpoint isEqualToString:_awsPlatformEndpoint.endpointArn]) {
AWSSNSUnsubscribeInput *unsubscribeRequest = [AWSSNSUnsubscribeInput new];
unsubscribeRequest.subscriptionArn = subscription.subscriptionArn;
pendingUnsubscribe++;
[[[_awsSnsClient unsubscribe:unsubscribeRequest] continueWithSuccessBlock:^id(BFTask *task) {
DLog(@"Unsubscribed from:%@",subscription.subscriptionArn);
return nil;
}] continueWithBlock:^id(BFTask *task) {
pendingUnsubscribe--;
if (task.error) {
// failed with error
ALog(@"Error unsubscribing to: %@, %@, %@", subscription, [task.error localizedDescription], [task.error localizedFailureReason]);
}
// If we have processed the last unsubscribe, then create topics and subscribe
if (sentLastUnsubscribe && (pendingUnsubscribe <= 0)) {
[self awsCreateTopicsAndSubscriptions];
}
return nil;
}];
}
}
request.nextToken = nextToken;
if(!nextToken) sentLastUnsubscribe = YES; // Reached the end of the SNS subscriptions
return nil;
}] continueWithBlock:^id(BFTask *task) {
if (task.error) {
// failed with error
ALog(@"Error listing subscriptions: %@, %@", [task.error localizedDescription], [task.error localizedFailureReason]);
}
return nil;
}];
} while (!sentLastUnsubscribe);
}
是否有更好的方法从端点删除所有 SNS 订阅?有没有办法在这个循环中强制释放任何保留的数据?
解决方案
同时使用递归和- waitUntilFinished
。内存负载现在 低很多 。
-(void) awsUnsubscribeFromAllSubscriptionsWithNextToken:(NSString *)nextToken AndSubscribe:(BOOL)subscribe {
AWSSNSListSubscriptionsInput *request = [AWSSNSListSubscriptionsInput new];
request.nextToken = nextToken;
[[[_awsSnsClient listSubscriptions:request] continueWithSuccessBlock:^id(BFTask *task) {
AWSSNSListSubscriptionsResponse *response = (AWSSNSListSubscriptionsResponse *)task.result;
NSArray *subscriptions = response.subscriptions;
for (AWSSNSSubscription *subscription in subscriptions) {
if ([subscription.endpoint isEqualToString:_awsPlatformEndpoint.endpointArn]) {
DLog(@"Unsubscribe from %@", subscription.topicArn);
AWSSNSUnsubscribeInput *unsubscribeRequest = [AWSSNSUnsubscribeInput new];
unsubscribeRequest.subscriptionArn = subscription.subscriptionArn;
[[_awsSnsClient unsubscribe:unsubscribeRequest] waitUntilFinished];
}
}
if(response.nextToken) {
[self awsUnsubscribeFromAllSubscriptionsWithNextToken:response.nextToken AndSubscribe:subscribe];
} else if (subscribe) {
[self awsCreateTopicsAndSubscriptions];
}
return nil;
}] continueWithBlock:^id(BFTask *task) {
if (task.error) {
// failed with error
ALog(@"Error listing subscriptions: %@, %@", [task.error localizedDescription], [task.error localizedFailureReason]);
}
return nil;
}];
}
请注意,- listSubscriptions:
是一个异步方法,returns 是立即执行的。您正在 for 循环中调用异步方法。这意味着您可能会在短时间内调用 - listSubscriptions:
数百次甚至数千次。
修复它最简单的方法是在异步块的末尾调用 - waitUntilFinished
。它使整个方法同步
但是,一般情况下,您应该尽可能避免调用 - waitUntilFinished
。 AWSKinesisRecorderTests.m
有一个函数可以递归调用- getRecords:
。您可以采用类似的模式。
问题
当我取消订阅我的端点订阅时,仪器内存图从 32MB 变为 300MB 并且应用程序被杀死 Jetsam。据我了解,AWS BFTasks 会自行清理……如果我删除 do...while
并允许它处理前 100 个订阅,一切正常。
进程
我的开发应用程序中有大约 8000 个订阅,这些应用程序使用 AWS SNS 将警报推送到 iOS 设备和电子邮件。在用户选择他们想要的订阅后,我删除他们现有的订阅,然后订阅新的订阅。 [_awsSnsClient listSubscriptions:request]
returns 最多 100 个订阅,我遍历这些,根据需要执行 [_awsSnsClient unsubscribe:unsubscribeRequest]
。然后获得下一个 100...
代码
- (void) awsUsubscribeAllSubscriptions {
DLog(@"Removing existing subscriptions");
AWSSNSListSubscriptionsInput *request = [AWSSNSListSubscriptionsInput new];
__block Boolean sentLastUnsubscribe = NO; // YES when the last unsubscribe request was sent
__block int pendingUnsubscribe = 0; // Number of outsianding unsubscribes (async process not yet complete)
do {
[[[_awsSnsClient listSubscriptions:request] continueWithSuccessBlock:^id(BFTask *task) {
AWSSNSListSubscriptionsResponse *response = (AWSSNSListSubscriptionsResponse *)task.result;
NSString *nextToken = response.nextToken;
NSArray *subscriptions = response.subscriptions;
for (AWSSNSSubscription *subscription in subscriptions) {
if ([subscription.endpoint isEqualToString:_awsPlatformEndpoint.endpointArn]) {
AWSSNSUnsubscribeInput *unsubscribeRequest = [AWSSNSUnsubscribeInput new];
unsubscribeRequest.subscriptionArn = subscription.subscriptionArn;
pendingUnsubscribe++;
[[[_awsSnsClient unsubscribe:unsubscribeRequest] continueWithSuccessBlock:^id(BFTask *task) {
DLog(@"Unsubscribed from:%@",subscription.subscriptionArn);
return nil;
}] continueWithBlock:^id(BFTask *task) {
pendingUnsubscribe--;
if (task.error) {
// failed with error
ALog(@"Error unsubscribing to: %@, %@, %@", subscription, [task.error localizedDescription], [task.error localizedFailureReason]);
}
// If we have processed the last unsubscribe, then create topics and subscribe
if (sentLastUnsubscribe && (pendingUnsubscribe <= 0)) {
[self awsCreateTopicsAndSubscriptions];
}
return nil;
}];
}
}
request.nextToken = nextToken;
if(!nextToken) sentLastUnsubscribe = YES; // Reached the end of the SNS subscriptions
return nil;
}] continueWithBlock:^id(BFTask *task) {
if (task.error) {
// failed with error
ALog(@"Error listing subscriptions: %@, %@", [task.error localizedDescription], [task.error localizedFailureReason]);
}
return nil;
}];
} while (!sentLastUnsubscribe);
}
是否有更好的方法从端点删除所有 SNS 订阅?有没有办法在这个循环中强制释放任何保留的数据?
解决方案
同时使用递归和- waitUntilFinished
。内存负载现在 低很多 。
-(void) awsUnsubscribeFromAllSubscriptionsWithNextToken:(NSString *)nextToken AndSubscribe:(BOOL)subscribe {
AWSSNSListSubscriptionsInput *request = [AWSSNSListSubscriptionsInput new];
request.nextToken = nextToken;
[[[_awsSnsClient listSubscriptions:request] continueWithSuccessBlock:^id(BFTask *task) {
AWSSNSListSubscriptionsResponse *response = (AWSSNSListSubscriptionsResponse *)task.result;
NSArray *subscriptions = response.subscriptions;
for (AWSSNSSubscription *subscription in subscriptions) {
if ([subscription.endpoint isEqualToString:_awsPlatformEndpoint.endpointArn]) {
DLog(@"Unsubscribe from %@", subscription.topicArn);
AWSSNSUnsubscribeInput *unsubscribeRequest = [AWSSNSUnsubscribeInput new];
unsubscribeRequest.subscriptionArn = subscription.subscriptionArn;
[[_awsSnsClient unsubscribe:unsubscribeRequest] waitUntilFinished];
}
}
if(response.nextToken) {
[self awsUnsubscribeFromAllSubscriptionsWithNextToken:response.nextToken AndSubscribe:subscribe];
} else if (subscribe) {
[self awsCreateTopicsAndSubscriptions];
}
return nil;
}] continueWithBlock:^id(BFTask *task) {
if (task.error) {
// failed with error
ALog(@"Error listing subscriptions: %@, %@", [task.error localizedDescription], [task.error localizedFailureReason]);
}
return nil;
}];
}
请注意,- listSubscriptions:
是一个异步方法,returns 是立即执行的。您正在 for 循环中调用异步方法。这意味着您可能会在短时间内调用 - listSubscriptions:
数百次甚至数千次。
修复它最简单的方法是在异步块的末尾调用 - waitUntilFinished
。它使整个方法同步
但是,一般情况下,您应该尽可能避免调用 - waitUntilFinished
。 AWSKinesisRecorderTests.m
有一个函数可以递归调用- getRecords:
。您可以采用类似的模式。