All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.theupswell.appengine.counter.data.CounterShardData Maven / Gradle / Ivy

/**
 * Copyright (C) 2014 UpSwell LLC ([email protected])
 *
 * 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.theupswell.appengine.counter.data;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Unindex;

/**
 * Represents a shard for a named counter in the datastore. An individual shard is written to infrequently to allow the
 * counter in aggregate to be incremented rapidly. All shard counts for a given named counter will be added together to
 * assemble the actual count for any named counter.
 * 
 * @author David Fuelling
 */
@Entity
// Not cached via @Cache because the this counter provides its own caching mechanism.
@Getter
@Setter
@Unindex
@ToString
@EqualsAndHashCode(of = "id")
public class CounterShardData
{
	static final String COUNTER_SHARD_KEY_SEPARATOR = "-";

	// The id of a CounterShardData is the name of the counter (for easy lookup
	// via a starts-with query) combined with the shardNumber. E.g.,
	// "CounterName-2" would be the counter with name "CounterName" and Shard
	// number 2.
	@Id
	private String id;

	// The total of this shard's count (not the total counter count)
	@Unindex
	private long count;

	@Unindex
	private DateTime creationDateTime;

	@Unindex
	private DateTime updatedDateTime;

	/**
	 * Default Constructor for Objectify
	 * 
	 * @deprecated Exists only for Objectify. Use the param-based constructors instead.
	 */
	@Deprecated
	public CounterShardData()
	{
		// Implemented for Objectify
	}

	/**
	 * Param-based Constructor
	 * 
	 * @param counterName
	 * @param shardNumber
	 */
	public CounterShardData(final String counterName, final int shardNumber)
	{
		// Preconditions checked by #constructCounterShardIdentifier
		this.setId(constructCounterShardIdentifier(counterName, shardNumber));

		this.creationDateTime = DateTime.now(DateTimeZone.UTC);
		this.updatedDateTime = DateTime.now(DateTimeZone.UTC);
	}

	/**
	 * Param-based Constructor
	 *
	 * @param counterShardDataKey
	 */
	public CounterShardData(final Key counterShardDataKey)
	{
		Preconditions.checkNotNull(counterShardDataKey);
		setId(counterShardDataKey.getName());
	}

	// /////////////////////////
	// Getters/Setters
	// /////////////////////////

	/**
	 * @return The last dateTime that an increment occurred.
	 */
	public DateTime getLastIncrement()
	{
		return getUpdatedDateTime();
	}

	/**
	 * Set the amount of this shard with a new {@code count}.
	 * 
	 * @param count
	 */
	public void setCount(long count)
	{
		this.count = count;
		this.setUpdatedDateTime(DateTime.now(DateTimeZone.UTC));
	}

	/**
	 * Assembles the Key for this entity. If an Entity has a Parent Key, that key will be included in the returned Key
	 * heirarchy.
	 *
	 * @return
	 */
	public Key getTypedKey()
	{
		return Key.create(CounterShardData.class, this.getId());
	}

	/**
	 * Assembles the Key for this entity. If an Entity has a Parent Key, that key will be included in the returned Key
	 * heirarchy.
	 */
	public com.google.appengine.api.datastore.Key getKey()
	{
		return this.getTypedKey().getRaw();
	}

	/**
	 * Helper method to set the internal identifier for this entity.
	 * 
	 * @param counterName
	 * @param shardNumber A unique identifier to distinguish shards for the same {@code counterName} from each other.
	 */
	@VisibleForTesting
	static String constructCounterShardIdentifier(final String counterName, final int shardNumber)
	{
		Preconditions.checkNotNull(counterName);
		Preconditions.checkArgument(!StringUtils.isBlank(counterName),
			"CounterData Names may not be null, blank, or empty!");
		Preconditions.checkArgument(shardNumber >= 0, "shardNumber must be greater than or equal to 0!");

		return counterName + COUNTER_SHARD_KEY_SEPARATOR + shardNumber;
	}

	/**
	 * Create a {@link Key Key}. Keys for this entity are not "parented" so that they can be added
	 * under high volume load in a given application. Note that CounterData will be in a namespace specific.
	 *
	 * @param counterDataKey
	 * @param shardNumber
	 * @return
	 */
	public static Key key(final Key counterDataKey, final int shardNumber)
	{
		// Preconditions checked by #constructCounterShardIdentifier
		return Key.create(CounterShardData.class,
			constructCounterShardIdentifier(counterDataKey.getName(), shardNumber));
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy