/* * Copyright 2017 HugeGraph Authors * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with this * work for additional information regarding copyright ownership. The ASF * licenses this file to You 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.baidu.hugegraph.api.graph; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicInteger; import org.apache.tinkerpop.gremlin.structure.Element; import org.slf4j.Logger; import com.baidu.hugegraph.HugeException; import com.baidu.hugegraph.HugeGraph; import com.baidu.hugegraph.api.API; import com.baidu.hugegraph.config.HugeConfig; import com.baidu.hugegraph.config.ServerOptions; import com.baidu.hugegraph.define.Checkable; import com.baidu.hugegraph.define.UpdateStrategy; import com.baidu.hugegraph.metrics.MetricsUtil; import com.baidu.hugegraph.server.RestServer; import com.baidu.hugegraph.structure.HugeElement; import com.baidu.hugegraph.util.E; import com.baidu.hugegraph.util.Log; import com.codahale.metrics.Meter; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; public class BatchAPI extends API { private static final Logger LOG = Log.logger(BatchAPI.class); // NOTE: VertexAPI and EdgeAPI should share a counter private static final AtomicInteger batchWriteThreads = new AtomicInteger(0); static { MetricsUtil.registerGauge(RestServer.class, "batch-write-threads", () -> batchWriteThreads.intValue()); } private final Meter batchMeter; public BatchAPI() { this.batchMeter = MetricsUtil.registerMeter(this.getClass(), "batch-commit"); } public <R> R commit(HugeConfig config, HugeGraph g, int size, Callable<R> callable) { int maxWriteThreads = config.get(ServerOptions.MAX_WRITE_THREADS); int writingThreads = batchWriteThreads.incrementAndGet(); if (writingThreads > maxWriteThreads) { batchWriteThreads.decrementAndGet(); throw new HugeException("The rest server is too busy to write"); } LOG.debug("The batch writing threads is {}", batchWriteThreads); try { R result = commit(g, callable); this.batchMeter.mark(size); return result; } finally { batchWriteThreads.decrementAndGet(); } } @JsonIgnoreProperties(value = {"type"}) protected static abstract class JsonElement implements Checkable { @JsonProperty("id") public Object id; @JsonProperty("label") public String label; @JsonProperty("properties") public Map<String, Object> properties; @JsonProperty("type") public String type; @Override public abstract void checkCreate(boolean isBatch); @Override public abstract void checkUpdate(); protected abstract Object[] properties(); } protected void updateExistElement(JsonElement oldElement, JsonElement newElement, Map<String, UpdateStrategy> strategies) { if (oldElement == null) { return; } E.checkArgument(newElement != null, "The json element can't be null"); for (Map.Entry<String, UpdateStrategy> kv : strategies.entrySet()) { String key = kv.getKey(); UpdateStrategy updateStrategy = kv.getValue(); if (oldElement.properties.get(key) != null && newElement.properties.get(key) != null) { Object value = updateStrategy.checkAndUpdateProperty( oldElement.properties.get(key), newElement.properties.get(key)); newElement.properties.put(key, value); } else if (oldElement.properties.get(key) != null && newElement.properties.get(key) == null) { // If new property is null & old is present, use old property newElement.properties.put(key, oldElement.properties.get(key)); } } } protected void updateExistElement(HugeGraph g, Element oldElement, JsonElement newElement, Map<String, UpdateStrategy> strategies) { if (oldElement == null) { return; } E.checkArgument(newElement != null, "The json element can't be null"); for (Map.Entry<String, UpdateStrategy> kv : strategies.entrySet()) { String key = kv.getKey(); UpdateStrategy updateStrategy = kv.getValue(); if (oldElement.property(key).isPresent() && newElement.properties.get(key) != null) { Object value = updateStrategy.checkAndUpdateProperty( oldElement.property(key).value(), newElement.properties.get(key)); value = g.propertyKey(key).validValueOrThrow(value); newElement.properties.put(key, value); } else if (oldElement.property(key).isPresent() && newElement.properties.get(key) == null) { // If new property is null & old is present, use old property newElement.properties.put(key, oldElement.property(key).value()); } } } protected static void updateProperties(HugeElement element, JsonElement jsonElement, boolean append) { for (Map.Entry<String, Object> e : jsonElement.properties.entrySet()) { String key = e.getKey(); Object value = e.getValue(); if (append) { element.property(key, value); } else { element.property(key).remove(); } } } }