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

org.hibernate.cache.infinispan.util.Tombstone Maven / Gradle / Ivy

There is a newer version: 5.6.15.Final
Show newest version
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.cache.infinispan.util;

import org.infinispan.commons.marshall.AdvancedExternalizer;
import org.infinispan.filter.KeyValueFilter;
import org.infinispan.metadata.Metadata;

import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Collections;
import java.util.Set;
import java.util.UUID;

/**
 * This is used both as the storage in entry, and for efficiency also directly in the cache.put() commands.
 *
 * @author Radim Vansa <[email protected]>
 */
public class Tombstone {
	public static final ExcludeTombstonesFilter EXCLUDE_TOMBSTONES = new ExcludeTombstonesFilter();

	// the format of data is repeated (timestamp, UUID.LSB, UUID.MSB)
	private final long[] data;

	public Tombstone(UUID uuid, long timestamp) {
		this.data = new long[] { timestamp, uuid.getLeastSignificantBits(), uuid.getMostSignificantBits() };
	}

	private Tombstone(long[] data) {
		this.data = data;
	}

	public long getLastTimestamp() {
		long max = data[0];
		for (int i = 3; i < data.length; i += 3) {
			max = Math.max(max, data[i]);
		}
		return max;
	}

	@Override
	public String toString() {
		final StringBuilder sb = new StringBuilder("Tombstone{");
		for (int i = 0; i < data.length; i += 3) {
			if (i != 0) {
				sb.append(", ");
			}
			sb.append(new UUID(data[i + 2], data[i + 1])).append('=').append(data[i]);
		}
		sb.append('}');
		return sb.toString();
	}

	public Tombstone merge(Tombstone update) {
		assert update != null;
		assert update.data.length == 3;
		int toRemove = 0;
		for (int i = 0; i < data.length; i += 3) {
			if (data[i] < update.data[0]) {
				toRemove += 3;
			}
			else if (update.data[1] == data[i + 1] && update.data[2] == data[i + 2]) {
				// UUID matches - second update during retry?
				toRemove += 3;
			}
		}
		if (data.length == toRemove) {
			// applying the update second time?
			return update;
		}
		else {
			long[] newData = new long[data.length - toRemove + 3]; // 3 for the update
			int j = 0;
			boolean uuidMatch = false;
			for (int i = 0; i < data.length; i += 3) {
				if (data[i] < update.data[0]) {
					// This is an old eviction
					continue;
				}
				else if (update.data[1] == data[i + 1] && update.data[2] == data[i + 2]) {
					// UUID matches
					System.arraycopy(update.data, 0, newData, j, 3);
					uuidMatch = true;
					j += 3;
				}
				else {
					System.arraycopy(data, i, newData, j, 3);
					j += 3;
				}
			}
			assert (uuidMatch && j == newData.length) || (!uuidMatch && j == newData.length - 3);
			if (!uuidMatch) {
				System.arraycopy(update.data, 0, newData, j, 3);
			}
			return new Tombstone(newData);
		}
	}

	public Object applyUpdate(UUID uuid, long timestamp, Object value) {
		int toRemove = 0;
		for (int i = 0; i < data.length; i += 3) {
			if (data[i] < timestamp) {
				toRemove += 3;
			}
			else if (uuid.getLeastSignificantBits() == data[i + 1] && uuid.getMostSignificantBits() == data[i + 2]) {
				toRemove += 3;
			}
		}
		if (data.length == toRemove) {
			if (value == null) {
				return new Tombstone(uuid, timestamp);
			}
			else {
				return value;
			}
		}
		else {
			long[] newData = new long[data.length - toRemove + 3]; // 3 for the update
			int j = 0;
			boolean uuidMatch = false;
			for (int i = 0; i < data.length; i += 3) {
				if (data[i] < timestamp) {
					// This is an old eviction
					continue;
				}
				else if (uuid.getLeastSignificantBits() == data[i + 1] && uuid.getMostSignificantBits() == data[i + 2]) {
					newData[j] = timestamp;
					newData[j + 1] = uuid.getLeastSignificantBits();
					newData[j + 2] = uuid.getMostSignificantBits();
					uuidMatch = true;
					j += 3;
				}
				else {
					System.arraycopy(data, i, newData, j, 3);
					j += 3;
				}
			}
			assert (uuidMatch && j == newData.length) || (!uuidMatch && j == newData.length - 3);
			if (!uuidMatch) {
				newData[j] = timestamp;
				newData[j + 1] = uuid.getLeastSignificantBits();
				newData[j + 2] = uuid.getMostSignificantBits();
			}
			return new Tombstone(newData);
		}
	}

	// Used only for testing purposes
	public int size() {
		return data.length / 3;
	}

	public static class Externalizer implements AdvancedExternalizer {
		@Override
		public Set> getTypeClasses() {
			return Collections.>singleton(Tombstone.class);
		}

		@Override
		public Integer getId() {
			return Externalizers.TOMBSTONE;
		}

		@Override
		public void writeObject(ObjectOutput output, Tombstone tombstone) throws IOException {
			output.writeInt(tombstone.data.length);
			for (int i = 0; i < tombstone.data.length; ++i) {
				output.writeLong(tombstone.data[i]);
			}
		}

		@Override
		public Tombstone readObject(ObjectInput input) throws IOException, ClassNotFoundException {
			int length = input.readInt();
			long[] data = new long[length];
			for (int i = 0; i < data.length; ++i) {
				data[i] = input.readLong();
			}
			return new Tombstone(data);
		}
	}

	public static class ExcludeTombstonesFilter implements KeyValueFilter {
		private ExcludeTombstonesFilter() {}

		@Override
		public boolean accept(Object key, Object value, Metadata metadata) {
			return !(value instanceof Tombstone);
		}
	}

	public static class ExcludeTombstonesFilterExternalizer implements AdvancedExternalizer {
		@Override
		public Set> getTypeClasses() {
			return Collections.>singleton(ExcludeTombstonesFilter.class);
		}

		@Override
		public Integer getId() {
			return Externalizers.EXCLUDE_TOMBSTONES_FILTER;
		}

		@Override
		public void writeObject(ObjectOutput output, ExcludeTombstonesFilter object) throws IOException {
		}

		@Override
		public ExcludeTombstonesFilter readObject(ObjectInput input) throws IOException, ClassNotFoundException {
			return EXCLUDE_TOMBSTONES;
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy