关于EclipseLink 2.5.0 HashPartition小BUG的关注

项目中使用了JPA Partition的方式进行分库,并没有采用阿里的那套读写分离(太繁琐、太繁琐...)。当下,最好的provider应该是hibernate,只可惜这项功能官方说是到5.0版本才能支持,碰巧eclipselink 2.5.0中实现了此功能,因此就选择了eclipse link。

其中的HashPartition说来也简单,就是把数据中涉及到分库字段的相关查询、持久化的操作指向到连接池的制定数据库上。说来应用起来也是比较简单。

@HashPartitioning(name="HashPartitionBySubId", partitionColumn=@Column(name="subId"), connectionPools={"default","node1"})
@Partitioned("HashPartitionBySubId")
public class SubEntity{

}


可能是使用正值数值做索引的缘故吧,2.5.0并没有注意到在做hash值得时候有可能出现负值的问题,因而会造成 ArrrayIndexBoundException 的错误。贴上代码一览:

org.eclipse.persistence.core-2.5.0.jar 包中 org.eclipse.persistence.descriptors.partitioning.HashPartitioningPolicy中:


    /**
     * INTERNAL:
     * Get a connection from one of the pools in a round robin rotation fashion.
     */
    public List<Accessor> getConnectionsForQuery(AbstractSession session, DatabaseQuery query, AbstractRecord arguments) {
        Object value = arguments.get(this.partitionField);
        if (value == null) {
            if (this.unionUnpartitionableQueries) {
                // Use all connections.
                List<Accessor> accessors = new ArrayList<Accessor>(this.connectionPools.size());
                for (String poolName : this.connectionPools) {
                    accessors.add(getAccessor(poolName, session, query, false));
                }
                return accessors;
            } else {
                // Use default behavior.
                return null;
            }
        }
        int index = value.hashCode() % this.connectionPools.size();
        if (session.getPlatform().hasPartitioningCallback()) {
            // UCP support.
            session.getPlatform().getPartitioningCallback().setPartitionId(index);
            return null;
        }
        // Use the mapped connection pool.
        List<Accessor> accessors = new ArrayList<Accessor>(1);
        String poolName = this.connectionPools.get(index);
        accessors.add(getAccessor(poolName, session, query, false));
        return accessors;

}

其中问题出在 varlue.hashCode()上,该内容可能为负值,通过mod计算后,出来的index成为负值,直接造成connectionPools.get(index)越界。

当下,好在2.5.2对这一BUG进行了修正。

    /**
     * INTERNAL:
     * Get a connection from one of the pools in a round robin rotation fashion.
     */
    public List<Accessor> getConnectionsForQuery(AbstractSession session, DatabaseQuery query, AbstractRecord arguments) {
        Object value = arguments.get(this.partitionField);
        if (value == null) {
            if (this.unionUnpartitionableQueries) {
                // Use all connections.
                List<Accessor> accessors = new ArrayList<Accessor>(this.connectionPools.size());
                for (String poolName : this.connectionPools) {
                    accessors.add(getAccessor(poolName, session, query, false));
                }
                return accessors;
            } else {
                // Use default behavior.
                return null;
            }
        }
        int index = Math.abs(value.hashCode()) % this.connectionPools.size();
        if (session.getPlatform().hasPartitioningCallback()) {
            // UCP support.
            session.getPlatform().getPartitioningCallback().setPartitionId(index);
            return null;
        }
        // Use the mapped connection pool.
        List<Accessor> accessors = new ArrayList<Accessor>(1);
        String poolName = this.connectionPools.get(index);
        accessors.add(getAccessor(poolName, session, query, false));
        return accessors;

}

所以如果使用JPA PARTITION功能的朋友,最好把版本提升到2.5.2.



郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。