ibm-mobilefirstmobilefirst-adapters

MobileFirst 8 : Unexpected error encountered while storing data


We are using UserAuthenticationSecurityCheck to authenticate user.

If verification is successful, the MFP server will store the user attributes.

public class AuthSecurityCheck extends UserAuthenticationSecurityCheck {

    static Logger logger = Logger.getLogger(AuthSecurityCheck.class.getName());

    private String userId, displayName;
    private JSONObject attrObject;

    private String errorMessage;

    @Override
    protected AuthenticatedUser createUser() {
        Map<String, Object> userAttrsMap = new HashMap<String, Object>();
        userAttrsMap.put("attributes",attrObject); 
        return new AuthenticatedUser(userId, displayName, this.getName(), userAttrsMap);
    }
...
}

But if we store larger data(when userAttrsMap is large enough), we will get the 500 error.

errorMsg: Unexpected error encountered while storing data

The error is shown below:

enter image description here

Full source is on Github: https://github.com/DannyYang/PMR_CreateUserStoredLargeData

MFP version:


Solution

  • The issue happens owing to the size of the data you are holding within the AuthenticatedUser object and thereby the Securitycheck's state.

    MFP runtime saves the state of the securitycheck along with all the attributes to the attribute store . This involves serializing the security check state and persisting it to the DB. With a large object ( the custom map you have) this persistence operation fails and ends in a transaction rollback. This happens because the data you are trying to persist is too big and exceeds the allocated size.

    SecurityCheck’s design consideration is to use it for a security check ( validation) and creating an identity object. Within your security check implementation, you have the following:

    //Here the large data is assigned to the variable.
    attrObject = JSONObject.parse(largeJSONString);
    
    //This data is set into the AuthenticatedUser object.
    Map<String, Object> userAttrsMap = new HashMap<String, Object>();
    userAttrsMap.put("attributes",attrObject);
    return new AuthenticatedUser(userId, displayName, this.getName(), userAttrsMap);
    

    In this scenario this large data becomes part of the Securitycheck itself and will be serialized and attempted for persistence into the attribute store. When this data does not fit in the column, the transaction is rolled back and the error condition is propagated to the end user. Hence the error message you see - ” Unexpected error occured while storing data”. Enabling detailed trace will indicate the actual cause of the issue in the server trace logs.

    Either way, this approach is not recommended at all in production systems because:

    a) Every request from the client reaching the server goes through security introspection which will involve the server to load, check and update the securitycheck’s state. On systems taking heavy load ( production ) this can and will have performance costs. The process involves serializing the data and deserializing it later. In a distributed topology ( cluster or farms ) the request may end up in any of the nodes and these nodes will have to load and later save the security check's state to the store. All this will impact performance of your system.

    b) At the end of successful authentication, the AuthenticatedUser object is propagated to the client application indicating completion of the login flow . Even if the SecurityCheck state were to be stored successfully in the attribute store ( with large data) transmitting large payloads over the network just to indicate successful login will be counter productive. For the enduser it may appear as if nothing has happened since they entered the credentials, while data indicating success is still getting downloaded.

    c) Under heavy loads , the server will be strained from both a) and b) above.

    You should consider cutting down the data that is propagated to the client within the authenticateduser object. Keep the data minimal within the AuthenticatedUser object. Instead, you should offload obtaining large data to resource adapters , that can be accessed post successful login .