Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remote config realtime listeners not removed #11458

Closed
mikehardy opened this issue Jun 19, 2023 · 7 comments · Fixed by #11461
Closed

remote config realtime listeners not removed #11458

mikehardy opened this issue Jun 19, 2023 · 7 comments · Fixed by #11461
Assignees

Comments

@mikehardy
Copy link
Contributor

mikehardy commented Jun 19, 2023

Description

It appears something is happening with the object equality of the listeners such that they are registered, and you can iterate over them with updates correctly sent, but when you go to remove them the listener is not found in the listeners set so it is not actually removed

I noticed odd behavior during the react-native-firebase implementation (invertase/react-native-firebase#7119) where I was getting more events than expected

I reached this conclusion by adding a bunch of logging prefixed with FirebaseRemoteConfig see snippet below

Hypothesis - either the wrong object is sent in to the remove call so it matches no listeners, or the listener address itself is somehow mutated while going into the dispatch_async block such that the set of listeners has slightly different addresses for the listener than that which is encapsulated in the registration's completion handler and thus isn't equal later when calling remove

Reproducing the issue

I think you can just add a listener, check the listeners count, remove the listener, then check the count again.

Firebase SDK Version

10.11.0

Xcode Version

14.3.1

Installation Method

CocoaPods

Firebase Product(s)

Remote Config

Targeted Platforms

iOS

Relevant Log Output

2023-06-19 15:17:00.104 Df testing[17169:26e92] RNFirebaseRemoteConfig onConfigUpdated called
2023-06-19 15:17:00.104 Df testing[17169:26e92] RNFirebaseRemoteConfig onConfigUpdated no native listener for this app yet. Registering native listener.
2023-06-19 15:17:00.105 Df testing[17169:26e92] FirebaseRemoteConfig addConfigUpdateListener called on <__NSMallocBlock__: 0x600003d6f1e0>
2023-06-19 15:17:00.106 Df testing[17169:26f13] FirebaseRemoteConfig addConfigUpdateListener added <__NSMallocBlock__: 0x600003c83d50>, count now 1
2023-06-19 15:17:00.106 Df testing[17169:26e92] FirebaseRemoteConfig listener creation, self is <FIRConfigUpdateListenerRegistration: 0x6000033558a0> / completionHandler is <__NSMallocBlock__: 0x600003c83780>
2023-06-19 15:17:00.106 Df testing[17169:26e92] FirebaseRemoteConfig addConfigUpdateListener returning FIRConfigUpdateListenerRegistration <FIRConfigUpdateListenerRegistration: 0x6000033558a0>
2023-06-19 15:17:00.109 Db testing[17169:27098] (Detox) 10.11.0 - [FirebaseRemoteConfig][I-RCN000039] Starting requesting token.
2023-06-19 15:17:00.130 I  testing[17169:27098] (Detox) 10.11.0 - [FirebaseRemoteConfig][I-RCN000022] Success to get iid : fVOpA6F880YiqXMzcImg5a.
2023-06-19 15:17:12.736 Df testing[17169:26e92] FirebaseRemoteConfig evaluateStreamResponse - fetch version 556
2023-06-19 15:17:12.736 Df testing[17169:26e92] FirebaseRemoteConfig autoFetch - fetch version 556
2023-06-19 15:17:12.736 Db testing[17169:26f4f] (Detox) 10.11.0 - [FirebaseRemoteConfig][I-RCN000015] Received config update message on stream.
2023-06-19 15:17:13.805 Db testing[17169:27098] (Detox) 10.11.0 - [FirebaseRemoteConfig][I-RCN000039] Starting requesting token.
2023-06-19 15:17:13.807 I  testing[17169:27098] (Detox) 10.11.0 - [FirebaseRemoteConfig][I-RCN000022] Success to get iid : fVOpA6F880YiqXMzcImg5a.
2023-06-19 15:17:13.808 Db testing[17169:27098] (Detox) 10.11.0 - [FirebaseRemoteConfig][I-RCN000060] Fetch with user properties completed.
2023-06-19 15:17:13.809 Db testing[17169:27098] (Detox) 10.11.0 - [FirebaseRemoteConfig][I-RCN000061] Fetch with user properties initiated.
2023-06-19 15:17:13.812 Db testing[17169:27098] (Detox) 10.11.0 - [FirebaseRemoteConfig][I-RCN000040] Start config fetch.
2023-06-19 15:17:13.812 Db testing[17169:27098] (Detox) 10.11.0 - [FirebaseRemoteConfig][I-RCN000061] Making remote config fetch.
2023-06-19 15:17:13.813 Db testing[17169:27098] (Detox) 10.11.0 - [FirebaseRemoteConfig][I-RCN000046] Making config request: https://firebaseremoteconfig.googleapis.com/v1/projects/react-native-firebase-testing/namespaces/firebase:fetch?key=AIzaSyAHAsf51D0A407EklG1bs-5wA7EbyfNFg0
2023-06-19 15:17:14.297 Db testing[17169:26f96] (Detox) 10.11.0 - [FirebaseRemoteConfig][I-RCN000050] config fetch completed. Error: nil StatusCode: 200
2023-06-19 15:17:14.297 Db testing[17169:26f96] (Detox) 10.11.0 - [FirebaseRemoteConfig][I-RCN000059] Updating config content from Response for namespace:firebase:__FIRAPP_DEFAULT with state: UPDATE
2023-06-19 15:17:14.299 Db testing[17169:26f96] (Detox) 10.11.0 - [FirebaseRemoteConfig][I-RCN000058] Update config in DB for namespace:firebase:__FIRAPP_DEFAULT
2023-06-19 15:17:14.301 Db testing[17169:27098] (Detox) 10.11.0 - [FirebaseRemoteConfig][I-RCN000056] Updating metadata with fetch result.
2023-06-19 15:17:14.389 Df testing[17169:26e92] FirebaseRemoteConfig - triggering 1 listeners
2023-06-19 15:17:14.399 Df testing[17169:26e92] FirebaseRemoteConfig - triggering listener <__NSMallocBlock__: 0x600003c83d50>
2023-06-19 15:17:14.405 Df testing[17169:26e92] RNFirebaseRemoteConfig onConfigUpdated event {
2023-06-19 15:17:14.588 Df testing[17169:26e92] RNFirebaseRemoteConfig removeConfigUpdateRegistration called
2023-06-19 15:17:14.589 Df testing[17169:26e92] RNFirebaseRemoteConfig there is a native listener for this app. Removing it.
2023-06-19 15:17:14.589 Df testing[17169:26e92] FirebaseRemoteConfig SDK listener removal, self is <FIRConfigUpdateListenerRegistration: 0x6000033558a0> / completionHandler is <__NSMallocBlock__: 0x600003c83780>
2023-06-19 15:17:14.589 Df testing[17169:26e92] FirebaseRemoteConfig removeConfigUpdateListener called on <__NSMallocBlock__: 0x600003c83780>
2023-06-19 15:17:14.590 Df testing[17169:26f4f] FirebaseRemoteConfig removeConfigUpdateListener <__NSMallocBlock__: 0x600003c83780>, before count 1
2023-06-19 15:17:14.590 Df testing[17169:26f4f] FirebaseRemoteConfig removeConfigUpdateListener, after count 1

If using Swift Package Manager, the project's Package.resolved

Expand Package.resolved snippet
Replace this line with the contents of your Package.resolved.

If using CocoaPods, the project's Podfile.lock

Expand Podfile.lock snippet
@google-oss-bot
Copy link

I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.

@mikehardy
Copy link
Contributor Author

Relative to my log entries, this method in particular is what makes me think something is happening with the address of listener between the sync part of the call and the dispatch_async block:

- (FIRConfigUpdateListenerRegistration *)addConfigUpdateListener:
(void (^_Nonnull)(FIRRemoteConfigUpdate *configUpdate, NSError *_Nullable error))listener {
if (listener == nil) {
return nil;
}
__weak RCNConfigRealtime *weakSelf = self;
dispatch_async(_realtimeLockQueue, ^{
__strong RCNConfigRealtime *strongSelf = weakSelf;
[strongSelf->_listeners addObject:listener];
[strongSelf beginRealtimeStream];
});
return [[FIRConfigUpdateListenerRegistration alloc] initWithClient:self
completionHandler:listener];
}

at line 688 the object is 0x600003d6f1e0
at line 695 it is 0x600003c83d50

0x600003c83d50 is what is seen when iterating listeners in response to an update

But 0x600003c83780 is shown as the completionHandler (listener?) in the registration, and that object is not found when attempting to remove the listener later

@mikehardy
Copy link
Contributor Author

Here's the file with the statements I added so you can drop it in for repro perhaps master...mikehardy:firebase-ios-sdk:patch-1

@mikehardy
Copy link
Contributor Author

mikehardy commented Jun 20, 2023

Okay, if I move what is at line 695 down out of the dispatch_async block and make the FIRConfigUpdateListenerRegistration a temporary variable then add to the _listeners set via the registration's completionHandler property, it works.

- (FIRConfigUpdateListenerRegistration *)addConfigUpdateListener:
    (void (^_Nonnull)(FIRRemoteConfigUpdate *configUpdate, NSError *_Nullable error))listener {
  if (listener == nil) {
    return nil;
  }
  NSLog(@"FirebaseRemoteConfig addConfigUpdateListener called on %@", listener);

  __weak RCNConfigRealtime *weakSelf = self;
  dispatch_async(_realtimeLockQueue, ^{
    __strong RCNConfigRealtime *strongSelf = weakSelf;
     /// cannot add the listener here, it is the wrong address and remove will not work?
    ///[strongSelf->_listeners addObject:listener];
    NSLog(@"FirebaseRemoteConfig addConfigUpdateListener added %@, count now %d", listener, strongSelf->_listeners.count);
    [strongSelf beginRealtimeStream];
  });

  /// new temporary variable here so we can access properties of the new registration
  FIRConfigUpdateListenerRegistration *foo = [[FIRConfigUpdateListenerRegistration alloc] initWithClient:self
                                                   completionHandler:listener];
  NSLog(@"FirebaseRemoteConfig addConfigUpdateListener returning FIRConfigUpdateListenerRegistration %@", foo);

  /// now add the listener via the property that we know we will try to remove later. Listener callbacks still work, remove works
  [self->_listeners addObject:foo.completionHandler];

  return foo;

...but then there's a new problem.

Now the stream is paused but it doesn't want to start back up again. So if I have a test that removes the last listener - causing the stream to pause - then immediately add a new listener, the stream doesn't reconnect.

I verified that was the next issue by commenting out the pauseRealtimeStream line here:

[strongSelf pauseRealtimeStream];

...and things started working again across listener add/remove/add, though this is obviously not optimal as now there is no way for the developer to shut the socket down when not in use.

@qdpham13
Copy link
Contributor

Hey @mikehardy thanks for bringing this to our attention. Let us take a look at this and get back to you.

@qdpham13
Copy link
Contributor

I think the above PR should address the issue. The problem that I found was that changes happening within the dispatch_async block were not consistent with what is outside of the block. Making a copy with __block looks to address it.

@mikehardy
Copy link
Contributor Author

left notes on the PR, but I tested the PR and it does resolve my issue. Excellent

@firebase firebase locked and limited conversation to collaborators Jul 21, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants