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

com.bigdata.rwstore.StorageStats Maven / Gradle / Ivy

/**

Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

Contact:
     SYSTAP, LLC DBA Blazegraph
     2501 Calvert ST NW #106
     Washington, DC 20008
     [email protected]

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package com.bigdata.rwstore;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Iterator;

import com.bigdata.rwstore.sector.SectorAllocator;

/**
 * Maintains stats on the RWStore allocations, useful for tuning Allocator
 * sizes and tracking store efficiency.
 * 
 * It can also track reallocation patterns that are lost in static snapshots
 * of current usage.
 * 
 * Stats are retained on external requests and also on internal allocator
 * use.
 * 
 * totalSlots represents the total reserved slots in all the AllocationBlocks
 * for the associated FixedAllocators.
 * 
 * slotAllocations is the total of all allocations made
 * 
 * slotDeletes is the total of all slot deletions
 * 
 * Therefore the total of currently allocated slots is 
 * 		slotAllocations - slotDeletes
 * 
 * sizeAllocations is the total in bytes of the actual data stored in the
 * slots
 * 
 * sizeDeletes is the total in bytes of the actual data that has been deleted
 * 
 * Therefore the size of the total in bytes stored in currently allocated slots is
 * 		sizeAllocations - sizeDeletes
 * 
 * @author Martyn Cutcher
 *
 */
public class StorageStats {
	final int cVersion = 0x0100;
	
	final int m_maxFixed;
	
	public class BlobBucket {
		final int m_size;
		long m_allocationSize;
		long m_allocations;
		long m_deletes;
		// See BLZG-1646 BytesAppData counter can not be tracked accurately and should be removed
//		long m_deleteSize;
		
		// By copying committed data the stats can be reset on abort
		BlobBucket m_committed = null;
		
		public BlobBucket(final int size) {
			m_size = size;
		}
		public BlobBucket(final DataInputStream instr) throws IOException {
			m_size = instr.readInt();
			m_allocationSize = instr.readLong();
			m_allocations = instr.readLong();
			// See BLZG-1646 BytesAppData counter can not be tracked accurately and should be removed
			instr.readLong(); // was m_deleteSize
//			m_deleteSize = instr.readLong();
			m_deletes = instr.readLong();
			
			commit();
		}
		
		public void write(final DataOutputStream outstr) throws IOException {
			outstr.writeInt(m_size);
			outstr.writeLong(m_allocationSize);
			outstr.writeLong(m_allocations);
            // See BLZG-1646 BytesAppData counter can not be tracked accurately and should be removed
            outstr.writeLong(0L); // was m_deleteSize
//            outstr.writeLong(m_deleteSize);
			outstr.writeLong(m_deletes);
		}
		public void commit() {
			if (m_committed == null) {
				m_committed = new BlobBucket(m_size);
			}
			m_committed.m_allocationSize = m_allocationSize;
			m_committed.m_allocations = m_allocations;
            // See BLZG-1646 BytesAppData counter can not be tracked accurately and should be removed
//			m_committed.m_deleteSize = m_deleteSize;
			m_committed.m_deletes = m_deletes;
		}
		
		public void reset() {
			if (m_committed != null) {
				m_allocationSize = m_committed.m_allocationSize;
				m_allocations = m_committed.m_allocations;
	            // See BLZG-1646 BytesAppData counter can not be tracked accurately and should be removed
//				m_deleteSize = m_committed.m_deleteSize;
				m_deletes = m_committed.m_deletes;
			} else {
				m_allocationSize = 0;
				m_allocations = 0;
	            // See BLZG-1646 BytesAppData counter can not be tracked accurately and should be removed
//				m_deleteSize = 0;
				m_deletes = 0;
			}
		}
		
		public void delete(final int sze) {
            // See BLZG-1646 BytesAppData counter can not be tracked accurately and should be removed
//			m_deleteSize += sze;
			m_deletes++;
		}
		public void allocate(final int sze) {
			m_allocationSize += sze;
			m_allocations++;
		}
		public long active() {
			return m_allocations - m_deletes;
		}
		public int meanAllocation() {
			if (m_allocations == 0)
				return 0;
			return (int) (m_allocationSize / m_allocations);
		}

		/*
		 * Remove because we lack the concept of reserved slots for blobs. Just
		 * look at the 8k allocators churn.
		 */
//		public float churn() {
//			
//		    if (active() == 0)
//				return m_allocations;
//			
//			final BigDecimal allocs = new BigDecimal(m_allocations);
//			
//			final BigDecimal used = new BigDecimal(active());			
//			
//			return allocs.divide(used, 2, RoundingMode.HALF_UP).floatValue();
//		}
		
	}
	
	public class Bucket {
		final int m_start;
		/** AllocatorSize: The #of bytes in the allocated slots issued by this allocator. */
		final int m_size;
		/** AllocatorCount: The #of fixed allocators for that slot size. */
		int m_allocators;
		/** SlotsReserved: The #of slots in this slot size which have had storage reserved for them. */
		long m_totalSlots;
		/** SlotsAllocated: Cumulative allocation of slots to date in this slot size (regardless of the transaction outcome). */
		long m_slotAllocations;
		/** SlotsRecycled: Cumulative recycled slots to date in this slot size (regardless of the transaction outcome). */
		long m_slotDeletes;
		/** The user bytes in use across all allocations for this slot size (does not consider recycled or deleted slots). */
		long m_sizeAllocations;
//		/**
//		 * The user bytes that were in use across all allocations for this slot
//		 * size that have been recycled / deleted.
//		 * 

// * Note: Per BLZG-1551, this value is not tracked accurately! // * // * @see BLZG-1551 (Storage statistics documentation and corrections) // * See BLZG-1646 BytesAppData counter can not be tracked accurately and should be removed // */ // long m_sizeDeletes; // By copying committed data the stats can be reset on abort Bucket m_committed = null; public Bucket(final int size, final int startRange) { m_size = size; m_start = startRange; } public Bucket(final DataInputStream instr) throws IOException { m_size = instr.readInt(); m_start = instr.readInt(); m_allocators = instr.readInt(); m_slotAllocations = instr.readLong(); m_slotDeletes = instr.readLong(); m_totalSlots = instr.readLong(); m_sizeAllocations = instr.readLong(); // See BLZG-1646 BytesAppData counter can not be tracked accurately and should be removed instr.readLong(); // was m_sizeDeletes // m_sizeDeletes = instr.readLong(); commit(); } public void write(final DataOutputStream outstr) throws IOException { outstr.writeInt(m_size); outstr.writeInt(m_start); outstr.writeInt(m_allocators); outstr.writeLong(m_slotAllocations); outstr.writeLong(m_slotDeletes); outstr.writeLong(m_totalSlots); outstr.writeLong(m_sizeAllocations); // See BLZG-1646 BytesAppData counter can not be tracked accurately and should be removed outstr.writeLong(0L); // was m_sizeDeletes // outstr.writeLong(m_sizeDeletes); } public void commit() { if (m_committed == null) { m_committed = new Bucket(m_size, m_start); } m_committed.m_allocators = m_allocators; m_committed.m_slotAllocations = m_slotAllocations; m_committed.m_slotDeletes = m_slotDeletes; m_committed.m_totalSlots = m_totalSlots; m_committed.m_sizeAllocations = m_sizeAllocations; // See BLZG-1646 BytesAppData counter can not be tracked accurately and should be removed // m_committed.m_sizeDeletes = m_sizeDeletes; } public void reset() { if (m_committed != null) { m_allocators = m_committed.m_allocators; m_slotAllocations = m_committed.m_slotAllocations; m_slotDeletes = m_committed.m_slotDeletes; m_totalSlots = m_committed.m_totalSlots; m_sizeAllocations = m_committed.m_sizeAllocations; // See BLZG-1646 BytesAppData counter can not be tracked accurately and should be removed // m_sizeDeletes = m_committed.m_sizeDeletes; } else { m_allocators = 0; m_slotAllocations = 0; m_slotDeletes = 0; m_totalSlots =0; m_sizeAllocations = 0; // See BLZG-1646 BytesAppData counter can not be tracked accurately and should be removed // m_sizeDeletes = 0; } } public void delete(final int sze) { if (sze < 0) throw new IllegalArgumentException("delete requires positive size, got: " + sze); // if (m_size > 64 && sze < 64) { // /* // * If called from deferFree then may not include size. If so // * then use average size of slots to date as best running // * estimate. // * // * @see BLZG-1551 (Storage statistics documentation and corrections) // */ // sze = meanAllocation(); // } if (sze > m_size) { // sze = ((sze - 1 + m_maxFixed)/ m_maxFixed) * 4; // Blob header throw new IllegalArgumentException("Deletion of address with size greater than slot - " + sze + " > " + m_size); } // See BLZG-1646 BytesAppData counter can not be tracked accurately and should be removed // m_sizeDeletes += sze; m_slotDeletes++; } public void allocate(final int sze) { if (sze <= 0) throw new IllegalArgumentException("allocate requires positive size, got: " + sze); m_sizeAllocations += sze; m_slotAllocations++; } public void addSlots(final int slots) { m_totalSlots += slots; } /** SlotsInUse: SlotsAllocated - SlotsRecycled (net slots in use for this slot size). */ public long usedSlots() { return m_slotAllocations - m_slotDeletes; } public long emptySlots() { return m_totalSlots - usedSlots(); } /** * BytesAppData: The #of bytes in the allocated slots which are used by * application data (including the record checksum). * * See BLZG-1551 : The data reported here used to be bad since * {@link #m_sizeDeletes} could not being tracked correctly in the * {@link StorageStats} because we do not know the actual #of bytes * in the slot that contain application data when we finally recycle * the slot. * * See BLZG-1646 BytesAppData counter can not be tracked accurately and should be removed */ public long usedStore() { return usedSlots() * m_size; // return m_sizeAllocations - m_sizeDeletes; } /** %SlotWaste: How well the application data fits in the slots (BytesAppData/(SlotsInUse*AllocatorSize)). */ public float slotWaste() { if (usedStore() == 0) return 0.0f; final BigDecimal size = new BigDecimal(reservedStore()); final BigDecimal store = new BigDecimal(100 * (reservedStore() - usedStore())); if(size.signum()==0) return 0f; return store.divide(size, 2, RoundingMode.HALF_UP).floatValue(); } /* * TODO This is invoked for both %TotalWaste and %FileWaste. Is it * correctly invoked for both statistics? Note that we do not have an * official definition of %TotalWaste. See BLZG-1551. */ public float totalWaste(final long total) { if (total == 0) return 0.0f; final long slotWaste = reservedStore() - usedStore(); final BigDecimal localWaste = new BigDecimal(100 * slotWaste); final BigDecimal totalWaste = new BigDecimal(total); if(totalWaste.signum()==0) return 0f; return localWaste.divide(totalWaste, 2, RoundingMode.HALF_UP).floatValue(); } /** * BytesReserved: The space reserved on the backing file for those allocation slots (AllocatorSlots * SlotsReserved). */ public long reservedStore() { return m_size * m_totalSlots; } public long reservedSlots() { return m_totalSlots; } public void addAlocator() { m_allocators++; } /** * SlotsChurn: A measure of how frequently slots of this size are * re-allocated provided by slotsAllocated/slotsReserved. This metric is * higher when there are more allocations made against a given #of slots * reserved. */ public float slotChurn() { final BigDecimal slotsAllocated = new BigDecimal(m_slotAllocations); final BigDecimal slotsReserved = new BigDecimal(m_totalSlots); if (slotsReserved.signum() == 0) return 0f; return slotsAllocated.divide(slotsReserved, 2, RoundingMode.HALF_UP).floatValue(); } /** %SlotsUnused: The percentage of slots of this size which are not in use (1-(SlotsInUse/SlotsReserved)). */ public float slotsUnused() { if (m_totalSlots == 0) { return 0.0f; } BigDecimal used = new BigDecimal(100 * (m_totalSlots-usedSlots())); BigDecimal total = new BigDecimal(m_totalSlots); if(total.signum()==0) return 0f; return used.divide(total, 2, RoundingMode.HALF_UP).floatValue(); } /** * %SlotsAllocated: SlotsAllocated/(Sum of SlotsAllocated across all * slot sizes). * * @param totalAllocations * The #of allocations across all slot sizes. * @return */ public float percentAllocations(final long totalAllocations) { if (totalAllocations == 0) { return 0.0f; } final BigDecimal used = new BigDecimal(100 * m_slotAllocations); final BigDecimal total = new BigDecimal(totalAllocations); if(total.signum()==0) return 0f; return used.divide(total, 2, RoundingMode.HALF_UP).floatValue(); } /** * %SlotsInUse: SlotsInUse / (total SlotsInUse across all slots sizes). * * @param totalInuse * The total of SlotsInUse across all slot sizes. */ public float percentSlotsInuse(final long totalInuse) { if (totalInuse == 0) { return 0.0f; } final BigDecimal used = new BigDecimal(100 * usedSlots()); final BigDecimal total = new BigDecimal(totalInuse); if(total.signum()==0) return 0f; return used.divide(total, 2, RoundingMode.HALF_UP).floatValue(); } /** * MeanAllocation: (total application bytes used across all allocations for this slot size) / SlotsAllocated * @return */ public int meanAllocation() { if (m_slotAllocations == 0) return 0; return (int) (m_sizeAllocations / m_slotAllocations); } } final ArrayList m_buckets; final ArrayList m_blobBuckets; // store total bytes allocated/deleted as blobs long m_blobAllocation; long m_blobDeletion; /** * * @param buckets - the slot sizes used by the FixedAllocators */ public StorageStats(final int[] buckets) { m_buckets = new ArrayList(); int prevLimit = 0; for (int i = 0; i < buckets.length; i++) { m_buckets.add(new Bucket(buckets[i]*64, prevLimit)); // slot sizes are 64 multiples prevLimit = buckets[i]*64; } // last fixed allocator needed to compute BlobBuckets m_maxFixed = m_buckets.get(buckets.length-1).m_size; m_blobBuckets = new ArrayList(); int curInc = m_maxFixed; int nxtBlob = m_maxFixed; final int cMaxBucket = 64 * 1024 * 1024; // 64 Mb while (nxtBlob < cMaxBucket) { nxtBlob += curInc; m_blobBuckets.add(new BlobBucket(nxtBlob)); curInc *= 2; } m_blobBuckets.add(new BlobBucket(Integer.MAX_VALUE)); // catch all } /** * * @param instr restore from reopen * * @throws IOException */ public StorageStats(final DataInputStream instr) throws IOException { int version = instr.readInt(); if (cVersion != version) { throw new IllegalStateException("StorageStats object is wrong version"); } m_buckets = new ArrayList(); int nbuckets = instr.readInt(); for (int i = 0; i < nbuckets; i++) { m_buckets.add(new Bucket(instr)); } m_maxFixed = m_buckets.get(m_buckets.size()-1).m_size; m_blobBuckets = new ArrayList(); int nblobbuckets = instr.readInt(); for (int i = 0; i < nblobbuckets; i++) { m_blobBuckets.add(new BlobBucket(instr)); } m_blobAllocation = instr.readLong(); m_blobDeletion = instr.readLong(); } public byte[] getData() throws IOException { ByteArrayOutputStream outb = new ByteArrayOutputStream(); DataOutputStream outd = new DataOutputStream(outb); outd.writeInt(cVersion); outd.writeInt(m_buckets.size()); for (Bucket b : m_buckets) { b.write(outd); } outd.writeInt(m_blobBuckets.size()); for (BlobBucket b : m_blobBuckets) { b.write(outd); } outd.writeLong(m_blobAllocation); outd.writeLong(m_blobDeletion); outd.flush(); return outb.toByteArray(); } public void allocateBlob(int sze) { m_blobAllocation += sze; // increment blob bucket findBlobBucket(sze).allocate(sze); } public void deleteBlob(int sze) { m_blobDeletion -= sze; // decrement blob bucket findBlobBucket(sze).delete(sze); } public Bucket findBucket(final int sze) { for (Bucket b : m_buckets) { if (sze == b.m_size) return b; } throw new IllegalStateException("Buckets have not been correctly set"); } private BlobBucket findBlobBucket(final int sze) { for (BlobBucket b : m_blobBuckets) { if (sze < b.m_size) return b; } throw new IllegalStateException("BlobBuckets have not been correctly set"); } public void register(FixedAllocator alloc, boolean init) { int block = alloc.getBlockSize(); for (Bucket b : m_buckets) { if (b.m_size == block) { alloc.setBucketStats(b); if (init) b.addAlocator(); return; } } throw new IllegalArgumentException("FixedAllocator with unexpected block size"); } public void register(FixedAllocator alloc) { register(alloc, false); } /** * Collected statistics are against each Allocation Block size: *

*
AllocatorSize
The #of bytes in the allocated slots issued by this allocator.
*
AllocatorCount
The #of fixed allocators for that slot size.
*
SlotsAllocated
Cumulative allocation of slots to date in this slot size (regardless of the transaction outcome).
*
%SlotsAllocated
SlotsAllocated/(Sum of SlotsAllocated across all slot sizes).
*
SlotsRecycled
Cumulative recycled slots to date in this slot size (regardless of the transaction outcome).
*
SlotsChurn
How frequently slots of this size are re-allocated (SlotsAllocated/SlotsReserved).
*
SlotsInUse
SlotsAllocated - SlotsRecycled (net slots in use for this slot size).
*
%SlotsInUse
SlotsInUse / (total SlotsInUse across all slots sizes).
*
MeanAllocation
((Total application bytes used across all allocations for this slot size) / SlotsAllocated).
*
SlotsReserved
The #of slots in this slot size which have had storage reserved for them.
*
%SlotsUnused
The percentage of slots of this size which are not in use (1-(SlotsInUse/SlotsReserved)).
*
BytesReserved
The space reserved on the backing file for those allocation slots (AllocatorSlots * SlotsReserved).
*
UsedStore
The #of bytes in the allocated slots.
*
%SlotWaste
How well the application data fits in the slots (BytesAppData/(SlotsInUse*AllocatorSize)).
*
%AppData
How much of your data is stored by each allocator (BytesAppData/Sum(BytesAppData)).
*
%StoreFile
How much of the backing file is reserved for each allocator (BytesReserved/Sum(BytesReserved)).
* TODO TotalWaste *
%StoreWaste
How much of the total waste on the store is waste for this allocator size ((BytesReserved-BytesAppData)/(Sum(BytesReserved)-Sum(BytesAppData))).
*
* * @param str The allocator statistics will be appended to the caller's buffer. * * @see BLZG-1646 BytesAppData counter can not be tracked accurately and should be removed */ public void showStats(final StringBuilder str) { str.append("\n-------------------------\n"); str.append("RWStore Allocator Summary\n"); str.append("-------------------------\n"); str.append(String.format("%-16s %16s %16s %16s %16s %16s %16s %16s %16s %16s %16s %16s %16s %16s %16s %16s %16s %16s \n", "AllocatorSize", "AllocatorCount", "SlotsAllocated", "%SlotsAllocated", "SlotsRecycled", "SlotChurn", "SlotsInUse", "%SlotsInUse", "MeanAllocation", "SlotsReserved", "%SlotsUnused", "BytesReserved", "BytesAppData", "%SlotWaste", "%AppData", "%StoreFile", "%TotalWaste", "%FileWaste" )); // aggregate over all slot sizes long totalAppData = 0; // total BytesAppData across all slot sizes. long totalFileStore = 0; // total BytesReserved across all slot sizes. long totalAllocations = 0; // total SlotsAllocated across all slot sizes. long totalInuse = 0; // total SlotsInUse across all slots sizes. for (Bucket b: m_buckets) { totalAppData += b.usedStore(); totalFileStore += b.reservedStore(); totalAllocations += b.m_slotAllocations; totalInuse += b.usedSlots(); } final long totalWaste = totalFileStore - totalAppData; for (Bucket b: m_buckets) { str.append(String.format("%-16d %16d %16d %16.2f %16d %16.2f %16d %16.2f %16d %16d %16.2f %16d %16d %16.2f %16.2f %16.2f %16.2f %16.2f \n", b.m_size, // AllocatorSize b.m_allocators, // AllocatorCount b.m_slotAllocations, // SlotsAllocated b.percentAllocations(totalAllocations), // %SlotsAllocated b.m_slotDeletes, // SlotsRecycled b.slotChurn(), // SlotChurn b.usedSlots(), // SlotsInUse b.percentSlotsInuse(totalInuse), // %SlotsInUse b.meanAllocation(), // MeanAllocation b.m_totalSlots, // SlotsReserved b.slotsUnused(), // %SlotsUnused b.reservedStore(), // BytesReserved b.usedStore(), // UsedStore b.slotWaste(), // %SlotWaste dataPercent(b.usedStore(), totalAppData), // %AppData dataPercent(b.reservedStore(), totalFileStore), // %StoreFile b.totalWaste(totalWaste), // %TotalWaste b.totalWaste(totalFileStore) // %FileWaste )); } str.append("\n-------------------------\n"); str.append("BLOBS\n"); str.append("-------------------------\n"); str.append(String.format("%-10s %12s %12s %12s %12s %12s\n",// %12s\n", "Bucket(K)", "Allocations", "Allocated", "Deletes", // "Deleted", // 4 "Current", // "Data", // 6 "Mean" // "Churn" )); for (BlobBucket b: m_blobBuckets) { str.append(String.format("%-10d %12d %12d %12d %12d %12d\n",// %12.2f\n", b.m_size/1024, // Bucket(K) b.m_allocations, // Allocations b.m_allocationSize, // Allocated b.m_deletes, // Deletes // b.m_deleteSize, // Deleted (b.m_allocations - b.m_deletes), // Current // (b.m_allocationSize - b.m_deleteSize), // Data b.meanAllocation() // Mean // b.churn() )); } } /** * Helper method returns the ratio (usedData/totalData) as a * percentage. * * @param usedData * @param totalData * @return */ static private float dataPercent(final long usedData, final long totalData) { if (totalData == 0) return 0.0f; final BigDecimal used = new BigDecimal(100 * usedData); final BigDecimal total = new BigDecimal(totalData); return used.divide(total, 2, RoundingMode.HALF_UP).floatValue(); } public void register(SectorAllocator allocator, boolean init) { throw new UnsupportedOperationException(); } public void commit() { for (Bucket b: m_buckets) { b.commit(); } for (BlobBucket b: m_blobBuckets) { b.commit(); } } public void reset() { for (Bucket b: m_buckets) { b.reset(); } for (BlobBucket b: m_blobBuckets) { b.reset(); } } public Iterator getBuckets() { return m_buckets.iterator(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy