/* * Copyright 2014 NAVER Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.navercorp.pinpoint.collector.dao.hbase; import com.navercorp.pinpoint.collector.dao.HostApplicationMapDao; import com.navercorp.pinpoint.common.server.util.AcceptedTimeService; import com.navercorp.pinpoint.collector.util.AtomicLongUpdateMap; import com.navercorp.pinpoint.common.buffer.AutomaticBuffer; import com.navercorp.pinpoint.common.buffer.Buffer; import com.navercorp.pinpoint.common.hbase.HBaseTables; import com.navercorp.pinpoint.common.hbase.HbaseOperations2; import com.navercorp.pinpoint.common.util.TimeSlot; import com.navercorp.pinpoint.common.util.TimeUtils; import com.sematext.hbase.wd.AbstractRowKeyDistributor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Repository; /** * * @author netspider * @author emeroad */ @Repository public class HbaseHostApplicationMapDao implements HostApplicationMapDao { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private HbaseOperations2 hbaseTemplate; @Autowired private AcceptedTimeService acceptedTimeService; @Autowired private TimeSlot timeSlot; @Autowired @Qualifier("acceptApplicationRowKeyDistributor") private AbstractRowKeyDistributor rowKeyDistributor; // FIXME should modify to save a cachekey at each 30~50 seconds instead of saving at each time private final AtomicLongUpdateMap<CacheKey> updater = new AtomicLongUpdateMap<>(); @Override public void insert(String host, String bindApplicationName, short bindServiceType, String parentApplicationName, short parentServiceType) { if (host == null) { throw new NullPointerException("host must not be null"); } if (bindApplicationName == null) { throw new NullPointerException("bindApplicationName must not be null"); } final long statisticsRowSlot = getSlotTime(); final CacheKey cacheKey = new CacheKey(host, bindApplicationName, bindServiceType, parentApplicationName, parentServiceType); final boolean needUpdate = updater.update(cacheKey, statisticsRowSlot); if (needUpdate) { insertHostVer2(host, bindApplicationName, bindServiceType, statisticsRowSlot, parentApplicationName, parentServiceType); } } private long getSlotTime() { final long acceptedTime = acceptedTimeService.getAcceptedTime(); return timeSlot.getTimeSlot(acceptedTime); } private void insertHostVer2(String host, String bindApplicationName, short bindServiceType, long statisticsRowSlot, String parentApplicationName, short parentServiceType) { if (logger.isDebugEnabled()) { logger.debug("Insert host-application map. host={}, bindApplicationName={}, bindServiceType={}, parentApplicationName={}, parentServiceType={}", host, bindApplicationName, bindServiceType, parentApplicationName, parentServiceType); } // TODO should consider to add bellow codes again later. //String parentAgentId = null; //final byte[] rowKey = createRowKey(parentApplicationName, parentServiceType, statisticsRowSlot, parentAgentId); final byte[] rowKey = createRowKey(parentApplicationName, parentServiceType, statisticsRowSlot, null); byte[] columnName = createColumnName(host, bindApplicationName, bindServiceType); try { hbaseTemplate.put(HBaseTables.HOST_APPLICATION_MAP_VER2, rowKey, HBaseTables.HOST_APPLICATION_MAP_VER2_CF_MAP, columnName, null); } catch (Exception ex) { logger.warn("retry one. Caused:{}", ex.getCause(), ex); hbaseTemplate.put(HBaseTables.HOST_APPLICATION_MAP_VER2, rowKey, HBaseTables.HOST_APPLICATION_MAP_VER2_CF_MAP, columnName, null); } } private byte[] createColumnName(String host, String bindApplicationName, short bindServiceType) { Buffer buffer = new AutomaticBuffer(); buffer.putPrefixedString(host); buffer.putPrefixedString(bindApplicationName); buffer.putShort(bindServiceType); return buffer.getBuffer(); } private byte[] createRowKey(String parentApplicationName, short parentServiceType, long statisticsRowSlot, String parentAgentId) { final byte[] rowKey = createRowKey0(parentApplicationName, parentServiceType, statisticsRowSlot, parentAgentId); return rowKeyDistributor.getDistributedKey(rowKey); } byte[] createRowKey0(String parentApplicationName, short parentServiceType, long statisticsRowSlot, String parentAgentId) { // even if a agentId be added for additional specifications, it may be safe to scan rows. // But is it needed to add parentAgentServiceType? final int SIZE = HBaseTables.APPLICATION_NAME_MAX_LEN + 2 + 8; final Buffer rowKeyBuffer = new AutomaticBuffer(SIZE); rowKeyBuffer.putPadString(parentApplicationName, HBaseTables.APPLICATION_NAME_MAX_LEN); rowKeyBuffer.putShort(parentServiceType); rowKeyBuffer.putLong(TimeUtils.reverseTimeMillis(statisticsRowSlot)); // there is no parentAgentId for now. if it added later, need to comment out below code for compatibility. // rowKeyBuffer.putPadString(parentAgentId, HBaseTables.AGENT_NAME_MAX_LEN); return rowKeyBuffer.getBuffer(); } private static final class CacheKey { private final String host; private final String applicationName; private final short serviceType; private final String parentApplicationName; private final short parentServiceType; public CacheKey(String host, String applicationName, short serviceType, String parentApplicationName, short parentServiceType) { if (host == null) { throw new NullPointerException("host must not be null"); } if (applicationName == null) { throw new NullPointerException("bindApplicationName must not be null"); } this.host = host; this.applicationName = applicationName; this.serviceType = serviceType; // may be null for below two parent values. this.parentApplicationName = parentApplicationName; this.parentServiceType = parentServiceType; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CacheKey cacheKey = (CacheKey) o; if (parentServiceType != cacheKey.parentServiceType) return false; if (serviceType != cacheKey.serviceType) return false; if (!applicationName.equals(cacheKey.applicationName)) return false; if (!host.equals(cacheKey.host)) return false; if (parentApplicationName != null ? !parentApplicationName.equals(cacheKey.parentApplicationName) : cacheKey.parentApplicationName != null) return false; return true; } @Override public int hashCode() { int result = host.hashCode(); result = 31 * result + applicationName.hashCode(); result = 31 * result + (int) serviceType; result = 31 * result + (parentApplicationName != null ? parentApplicationName.hashCode() : 0); result = 31 * result + (int) parentServiceType; return result; } } }