launchdarkly / cpp-sdks

C++ Client/Server SDKs
Other
5 stars 2 forks source link

"LDClientSDK_FlagNotifier_OnFlagChange" Function callback does not get call. #393

Closed ngangomsamananda closed 3 months ago

ngangomsamananda commented 3 months ago

We use LDClientSDK_FlagNotifier_OnFlagChange for the replacement of LDClientRegisterFeatureFlagListener while upgrading v2.5.2 to v3.4.2 by following the documentation https://docs.launchdarkly.com/sdk/features/flag-changes .

Describe the bug The Fuction callback which is set in "LDClientSDK_FlagNotifier_OnFlagChange" does not get trigger. So, v2.5.2 the Function callback which is set using LDClientRegisterFeatureFlagListener got trigger when the user context is updated but not in replacement provided for v3.4.2 (i.e. LDClientSDK_FlagNotifier_OnFlagChange)

To reproduce

#include <iostream>
#include <launchdarkly/client_side/bindings/c/sdk.h>
#include <launchdarkly/bindings/c/context_builder.h>
#include <launchdarkly/client_side/bindings/c/config/builder.h>
#include <launchdarkly/bindings/c/object_builder.h>
#include <launchdarkly/bindings/c/memory_routines.h>
#include <mutex>
#include <windows.h>

void FlagListenerFunction(char const* flagKey, LDValue newValue, LDValue oldValue, bool deleted, void* userData)
{
    bool newLDValue = LDValue_GetBool(newValue);
    bool oldLDValue = LDValue_GetBool(oldValue);
    if (deleted)
    {
        std::cout << "Flag deleted.\n";
    }
    else
    {
        std::cout << "Flag updated";
    }
}

int main()
{
    const char* userid = "user-id";
    const char* mobile_key = "mob-key-123-abc";
    LDClientConfigBuilder ConfigBuilder = LDClientConfigBuilder_New(mobile_key);
    LDClientConfigBuilder_Events_PrivateAttribute(ConfigBuilder, "hubId");

    LDClientConfig config;
    LDStatus status = LDClientConfigBuilder_Build(ConfigBuilder, &config);
    if (!LDStatus_Ok(status)) {
      std::cout<<"ldcsdk client config builder failed";
    }

    LDContextBuilder context_builder = LDContextBuilder_New();
    LDContextBuilder_AddKind(context_builder, "user", userid);

    LDContextBuilder_Attributes_SetPrivate(context_builder, "user", "email", LDValue_NewString("abc@gmail.com"));

    LDContext context = LDContextBuilder_Build(context_builder);
    LDClientSDK g_pLDClient = LDClientSDK_New(config, context);
    unsigned int maxwait = 10 * 1000; /* 10 seconds */

    bool initialized_successfully;
    if (LDClientSDK_Start(g_pLDClient, maxwait, &initialized_successfully)) 
    {
        if (initialized_successfully) 
        {
            std::cout << "LaunchDarkly: client Initialization Succeded." << std::endl;
        }
        else 
        {
            std::cout<<"LaunchDarkly: client Initialization failed\n";
        }
    } 
    else {
            std::cout<<"LaunchDarkly: The client is still initializing.\n";
    }

    bool initialized = LDClientSDK_Initialized(g_pLDClient);
    const char* flagName = "flag-name";
    bool defaultFlagValue = false;
    bool flagValue = false;
    if (initialized)
    {
        flagValue = LDClientSDK_BoolVariation(g_pLDClient, flagName, defaultFlagValue);
    }

    LDFlagListener listener;
    LDFlagListener_Init(&listener);
    listener.FlagChanged = FlagListenerFunction;
    LDListenerConnection connection = LDClientSDK_FlagNotifier_OnFlagChange(g_pLDClient, flagName, listener);
    LDListenerConnection_Disconnect(connection);
    LDListenerConnection_Free(connection);

    LDContextBuilder newContextBuilder = LDContextBuilder_New();
    LDContextBuilder_AddKind(newContextBuilder, "user", userid);
    LDContextBuilder_Attributes_SetPrivate(newContextBuilder, "user", "email", LDValue_NewString("abc123@gmail.com"));

    bool identified =false;
    LDContext updatedContext = LDContextBuilder_Build(newContextBuilder);
    // LDClientSDK_Identify(g_pLDClient, updated_context, LD_NONBLOCKING, NULL);

    if (LDClientSDK_Identify(g_pLDClient, updatedContext, 5000, &identified))
    {
        if (identified)
        {
            std::cout<<"LaunchDarkly: client identification Succeded.\n";
        }
        else
        {
            /* The specified timeout was reached, but the client is still identifying. */
        }
    }
    else
    {
        std::cout << "LaunchDarkly: The client is still identifying.\n";
    }

    // wait to checck for function call back
    std::condition_variable cv;
    std::mutex mtx;
    std::unique_lock<std::mutex> lck(mtx);
    cv.wait_for(lck, std::chrono::milliseconds(5000));

}

Expected behavior Function callback of LDClientSDK_FlagNotifier_OnFlagChange should get call just like LDClientRegisterFeatureFlagListener from v2.5.2.

Logs If applicable, add any log output related to your problem. To get more logs from the SDK, change the log level using environment variable LD_LOG_LEVEL. For example:

   LD_LOG_LEVEL=debug ./your-application

SDK version 3.4.2

Language version, developer tools For instance, C++17 or C11. If you are using a language that requires a separate compiler, such as C, please include the name and version of the compiler too.

OS/platform Windows 10

Additional context Add any other context about the problem here.

cwaldren-ld commented 3 months ago

Hi @ngangomsamananda , I see in your code you have:

 LDListenerConnection_Disconnect(connection);
 LDListenerConnection_Free(connection);

This will remove the flag listener, so you will not get any callbacks.

The first line, LDListenerConnection_Disconnect, disconnects the listener. The second line frees the memory of the listener.

I would recommend you: 1) Delete LDListenerConnection_Disconnect call 2) Move LDListenerConnection_Free to the end of the script.

Let me know if this resolves the problem. Please note, if the flag value is the same for both contexts you will not get an update.

ngangomsamananda commented 3 months ago

Hi @cwaldren-ld, I had tried what you had recommended but the issue is still not resolve. We use LDClientSDK_FlagNotifier_OnFlagChange for the replacement ofLDClientRegisterFeatureFlagListener since then we do not get the callbacks. Could you please verify if you get the callbacks of LDClientSDK_FlagNotifier_OnFlagChange? Thanks.

cwaldren-ld commented 3 months ago

Yes, I changed the code as I recommended, and I did receive the flag callback.

@ngangomsamananda Would you be able to continue this conversation with LaunchDarkly support? We could gain access to your environment and targeting rules to try and continue debugging.

ngangomsamananda commented 3 months ago

@cwaldren-ld sure, let me continue this conversation with support team. Thanks!

ngangomsamananda commented 3 months ago

@cwaldren-ld I have a query. We have a list of feature flags. Do I need to callLDListenerConnection_Free for each of the listener I set in LDClientSDK_FlagNotifier_OnFlagChange ? Something like below code.

for ( i=0; i<totalFlags; i++)
{
   LDFlagListener listener;
   LDFlagListener_Init(&listener);
   listener.FlagChanged = FlagListenerFunction;
   LDListenerConnection connection = LDClientSDK_FlagNotifier_OnFlagChange(g_pLDClient, totalFlags[i], listener);
   LDListenerConnection_Free(connection);
}
ngangomsamananda commented 3 months ago

@cwaldren-ld I am able to get the callbacks now. Thank you.

cwaldren-ld commented 3 months ago

Do I need to call LDListenerConnection_Free for each of the listener I set in LDClientSDK_FlagNotifier_OnFlagChange?

Yes, if you wish to avoid memory leak at runtime, you must call LDListenerConnection_Free for each listener.

This is because the connection is allocated on the heap and the pointer is returned to you, so you are responsible for deallocating it.

cwaldren-ld commented 3 months ago

I am able to get the callbacks now. Thank you.

That is good to hear. Would you mind explaining what was the problem, so I can assist other people if they have this problem?

ngangomsamananda commented 3 months ago

@cwaldren-ld Removed theLDListenerConnection_Disconnect(connection); from the code and also the flag value was same between the two contexts.

Thank you so much for your helps.