0

Getting below exception while updating entry in the cache while establishing Many to Many relation.

org.apache.geode.cache.UnsupportedOperationInTransactionException: Expected size of 1 {[/__PR/_B__User_101]} for target=192.168.1.2(cacheServer2:7756)<v1>:41001 during a distributed transaction but got 2 {[[], [DistTxThinEntryState:  ,regionVersion=2 ,tailKey=440 ,memberID=null]]}
at org.apache.geode.internal.cache.DistTXStateProxyImplOnCoordinator.populateEntryEventMap(DistTXStateProxyImplOnCoordinator.java:576)
at org.apache.geode.internal.cache.DistTXStateProxyImplOnCoordinator.doPrecommit(DistTXStateProxyImplOnCoordinator.java:484)
at org.apache.geode.internal.cache.DistTXStateProxyImplOnCoordinator.commit(DistTXStateProxyImplOnCoordinator.java:88)
at org.apache.geode.internal.cache.TXManagerImpl.commit(TXManagerImpl.java:426)
at com.trendcore.cache.peertopeer.service.UserServiceImpl.attachRoleToUser(UserServiceImpl.java:108)
at com.trendcore.cache.peertopeer.CacheApplication.attachRoleToUser(CacheApplication.java:121)

Cache Configuration -> It's Peer to Peer configration with 2 regions.

Properties properties = new Properties();
properties.setProperty("locators", "localhost[13489]");
properties.setProperty("mcast-address", "224.0.0.0");
properties.setProperty("mcast-port", "0");
properties.setProperty(NAME, "cacheServer1");

CacheFactory cacheFactory = new CacheFactory(this.cacheConfiguration);
cache = cacheFactory.create();

User Region

RegionFactory<Long, User> regionFactory = this.cache.createRegionFactory(RegionShortcut.PARTITION);
    userRegion = regionFactory.create(USER_REGION);

Role Region

RegionFactory<Long, Role> regionFactory = this.cache.createRegionFactory(RegionShortcut.PARTITION);
    roleRegion = regionFactory.create(ROLE_REGION);

User model resides in User region

public class User implements  Serializable{
    private Long id;
    private String username;
    private Map<Long,Object> roles;

    //Getters , Setters

    public void addRole(Long roleId) {
            roles.put(roleId,null);
    }
}

Role model resides in Role region

public class Role implements Serializable {

    private Long id;
    private String roleName;

    //getters , setters
}

Users and roles are inserted in the respective regions using below code.

public void insertUser(User user) {
    CacheTransactionManager cacheTransactionManager = cache.getCacheTransactionManager();
    try {
        cacheTransactionManager.begin();
        userRegion.put(user.getId(), user);
        cacheTransactionManager.commit();
    } catch (Exception e) {
        cacheTransactionManager.rollback();
    }
}



public void insertRole(Role role) {
    CacheTransactionManager cacheTransactionManager = cache.getCacheTransactionManager();
    try {
        cacheTransactionManager.begin();
        roleRegion.put(role.getId(), role);
        cacheTransactionManager.commit();
    } catch (Exception e) {
        cacheTransactionManager.rollback();
    }
}

When any roleIds are put in existing cache user object then above exception is thrown.

public void attachRoleToUser(Long userId, Long roleId) {
    Region<Long, User> userRegion = cache.getRegion(USER_REGION);
    Region<Long, Role> roleRegion = cache.getRegion("Role");
    CacheTransactionManager cacheTransactionManager = cache.getCacheTransactionManager();
    try {
        cacheTransactionManager.setDistributed(true);
        cacheTransactionManager.begin();

        Role role = roleRegion.get(roleId);

        if (role != null) {
            User user = userRegion.get(userId);
            user.addRole(role.getId());
            userRegion.put(userId,user);
        }
        cacheTransactionManager.commit();
    } catch (Exception e) {
        try {
            cacheTransactionManager.rollback();
        }catch (Exception rbe){

        }
        throw new RuntimeException(e);
    }
}

Any guidance in this case will be appreciated.

Anurag
  • 23
  • 4
  • Have you tried creating the regions as `co-located`?, it's a [requirement](https://geode.apache.org/docs/guide/111/developing/transactions/design_considerations.html#colocate-PRs) for executing transactions on `PARTITION` regions.You might also want to follow the recommendations highlighted in the [Code Examples](https://geode.apache.org/docs/guide/111/developing/transactions/directed_example.html) regarding **when to rollback a failed transaction** (instead of doing it for every exception). – Juan Ramos Jan 03 '20 at 15:46

2 Answers2

1

1.) Distributed transactions only work for Replicated Regions at this point.

2.) In the second case, only one region is now in the transaction boundary, so co-location is not needed.

3.) I think in your first case, you had two partitioned regions as part of a transaction, this requires the regions to be co-located ( the relevant data has to exist on the node, so it's also possible that one is a replicated region and the other is a partitioned)

Jason H
  • 11
  • 1
0

Changed, attach user to role method to below and there is no exception.

Region<Long, User> userRegion = cache.getRegion(USER_REGION);
    Region<Long, Role> roleRegion = cache.getRegion("Role");
    CacheTransactionManager cacheTransactionManager = cache.getCacheTransactionManager();
    try {
       //This is change fetching role information outside transaction boundry.
        Role role = roleRegion.get(roleId);

        cacheTransactionManager.setDistributed(true);
        cacheTransactionManager.begin();

        //This line is causing below exception
        //org.apache.geode.cache.UnsupportedOperationInTransactionException: Expected size of 1 {[/__PR/_B__User_101]}
        //Role role = roleRegion.get(roleId);

        if (role != null) {
            User user = userRegion.get(userId);
            user.addRole(role.getId());
            userRegion.put(userId,user);
        }
        cacheTransactionManager.commit();
    } catch (Exception e) {
        try {
            if(cacheTransactionManager != null && cacheTransactionManager.exists())
                cacheTransactionManager.rollback();
        }catch (Exception rbe){

        }
        throw new RuntimeException(e);
    }

And there is no exception.

However need more information on below points.

  1. Transaction was set to distributed.
  2. User and Role both regions were involved in this case transaction failed, with exception UnsupportedOperationInTransactionException
  3. Distributed transactions are not working on multiple regions.
Anurag
  • 23
  • 4