package org.apache.solr.handler.component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import org.apache.solr.common.params.FacetParams;
import org.apache.solr.common.params.MergeShards;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.handler.component.ResponseBuilder.ScheduleInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alimama.mdrill.utils.UniqConfig;
/**
* 海狗的查询调度
* @author yannian.mu
*
*/
public class MergerSchedule {
protected static Logger log = LoggerFactory.getLogger(MergerSchedule.class);
public static int countSize(HashMap<String, AssginShard> host2AssginShard)
{
int size=0;
for(Entry<String, AssginShard> e:host2AssginShard.entrySet())
{
size+=e.getValue().shards.size();
}
return size;
}
public static ScheduleInfo schedule(SolrParams params,
List<String> lst,
List<String> mslist, String[] shardpartions) {
ScheduleInfo scheduleInfo=new ScheduleInfo();
scheduleInfo.partions=shardpartions;
scheduleInfo.hasSubShards = false;
boolean isfacet = params.getBool(FacetParams.FACET_CROSS, false);
Integer maxshards = params.getInt(FacetParams.MERGER_MAX_SHARDS,UniqConfig.getMaxMergerShard());
HashMap<String, AssginShard> host2AssginShard=MergerSchedule.assignByHost(lst, mslist);
int hostsize=host2AssginShard.size();
int size=countSize(host2AssginShard);
if(maxshards >= size|| hostsize<=1|| !isfacet)
{
if(shardpartions!=null&&shardpartions.length>0)
{
//如果只剩下一个hosts的,需要将partions展开
for(Entry<String, AssginShard> e:host2AssginShard.entrySet())
{
e.getValue().expand(shardpartions);
}
scheduleInfo.partions=null;
log.info("MergerSchedule fill:"+size+"=="+host2AssginShard.toString());
}
}
size=countSize(host2AssginShard);
if ((maxshards >= size&&hostsize<=1) || !isfacet) {
ArrayList<String> shardlist=new ArrayList<String>();
for(Entry<String, AssginShard> e:host2AssginShard.entrySet())
{
shardlist.addAll(e.getValue().shards);
}
scheduleInfo.shards=shardlist.toArray(new String[shardlist.size()]);
log.info("MergerSchedule shards:"+Arrays.toString(scheduleInfo.shards));
return scheduleInfo;
}
ArrayList<AssginShard> result=new ArrayList<AssginShard>();
int requestCount=hostsize;
if(hostsize>maxshards)
{
requestCount=maxshards;
}
for (int i = 0; i < requestCount; i++) {
result.add(new AssginShard(new ArrayList<String>(), new ArrayList<String>()));
}
int selectindex = 0;
for (Entry<String, AssginShard> e : host2AssginShard.entrySet()) {
AssginShard as = e.getValue();
AssginShard toadd = result.get(selectindex % requestCount);
toadd.ms.addAll(as.ms);
toadd.shards.addAll(as.shards);
selectindex++;
}
if(hostsize<=1)
{
scheduleSingleHost(result.get(0), scheduleInfo, maxshards);
return scheduleInfo;
}
int shardcnt = result.size();
scheduleInfo.shards = new String[shardcnt];
scheduleInfo.subShards = new String[shardcnt];
scheduleInfo.hasSubShards = true;
for (int i = 0; i < shardcnt; i++) {
AssginShard as = result.get(i);
scheduleInfo.shards[i] = as.randomMerger();
StringBuffer subShards = new StringBuffer();
String joinchar = "";
for (String s : as.shards) {
subShards.append(joinchar);
subShards.append(s);
joinchar = ",";
}
scheduleInfo.subShards[i] = subShards.toString();
log.info("MergerSchedule host:"+scheduleInfo.shards[i] + "=>>"+ scheduleInfo.subShards[i]);
}
return scheduleInfo;
}
private static class AssginShard{
public List<String> ms=null;
public List<String> shards=null;
public AssginShard(List<String> ms, List<String> shards) {
super();
this.ms = ms;
this.shards = shards;
}
public void expand(String[] partions)
{
List<String> tmp=new ArrayList<String>(shards.size()*partions.length);
for(String s:this.shards)
{
for(String p:partions)
{
if(!p.isEmpty())
{
tmp.add(s.replaceAll("_mdrillshard_", p));
}
}
}
this.shards=tmp;
}
int index=-1;
public String randomMerger()
{
if(this.index<0)
{
this.index=(int) (Math.random() * 100000);
}
Integer pos = (this.index++ % this.ms.size());
return this.ms.get(pos);
}
@Override
public String toString() {
return "AssginShard [ms=" + ms + ", shards=" + shards + "]";
}
}
private static HashMap<String,AssginShard> assignByHost(List<String> lst,
List<String> mslist)
{
HashMap<String,ArrayList<String>> listByHost=MergeShards.getByHost(lst);
HashMap<String,ArrayList<String>> mslistByHost=MergeShards.getByHost(mslist);
HashMap<String,AssginShard> assign=new HashMap<String,AssginShard>();
//处理本机有merger server的情形
ArrayList<String> allmsHost=new ArrayList<String>();
for(Entry<String,ArrayList<String>> e:mslistByHost.entrySet())
{
String host=e.getKey();
allmsHost.add(host);
ArrayList<String> list=listByHost.remove(host);
if(list==null||list.size()<=0)
{
continue;
}
AssginShard assignlist=assign.get(host);
if(assignlist==null)
{
ArrayList<String> localMergerList=e.getValue();
assignlist=new AssginShard(localMergerList, new ArrayList<String>());
assign.put(host, assignlist);
}
assignlist.shards.addAll(list);
}
Collections.sort(allmsHost);
int mshostlength=allmsHost.size();
//处理本机没有merger server的情形
for(Entry<String,ArrayList<String>> e:listByHost.entrySet())
{
for(String s:e.getValue())
{
int hashcode=absHashcode(s.hashCode());
int index=hashcode%mshostlength;
String host=allmsHost.get(index);
AssginShard assignlist=assign.get(host);
if(assignlist==null)
{
List<String> val=mslistByHost.get(host);
assignlist=new AssginShard(val, new ArrayList<String>());
assign.put(host, assignlist);
}
assignlist.shards.add(s);
}
}
return assign;
}
private static int absHashcode(int hashcode )
{
if(hashcode<0)
{
hashcode*=-1;
}
return hashcode;
}
private static void scheduleSingleHost(AssginShard assign0,
ScheduleInfo scheduleInfo, Integer maxshards) {
int numshards = (assign0.shards.size() / maxshards) + 1;
if (numshards > maxshards) {
numshards = maxshards;
}
String[] ssubshards = MergeShards.split(assign0.shards, numshards);
scheduleInfo.shards = new String[ssubshards.length];
scheduleInfo.subShards = new String[ssubshards.length];
scheduleInfo.hasSubShards = true;
for (int i = 0; i < ssubshards.length; i++) {
scheduleInfo.shards[i] = assign0.randomMerger();
scheduleInfo.subShards[i] = ssubshards[i];
log.info("shedule by single:" + scheduleInfo.shards[i] + "=>>" + scheduleInfo.subShards[i]);
}
log.info("MergerSchedule scheduleSingleHost:"+ scheduleInfo.shards.length + ">>" + numshards);
}
}