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

org.voltdb.utils.CatalogSizing Maven / Gradle / Ivy

There is a newer version: 10.1.1
Show newest version
/* This file is part of VoltDB.
 * Copyright (C) 2008-2020 VoltDB Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with VoltDB.  If not, see .
 */

package org.voltdb.utils;

import java.util.ArrayList;
import java.util.List;

import org.voltdb.VoltType;
import org.voltdb.catalog.CatalogMap;
import org.voltdb.catalog.Column;
import org.voltdb.catalog.ColumnRef;
import org.voltdb.catalog.Database;
import org.voltdb.catalog.Index;
import org.voltdb.catalog.Table;
import org.voltdb.types.IndexType;

/**
 * A collection of classes used for estimating the bytewise size of a database,
 * given a catalog.
 *
 * Currently used by the Compiler Report.
 *
 */
public abstract class CatalogSizing {
    public final static int MAX_BYTES_PER_UTF8_CHARACTER = 4;

    /**
     * Base class for raw catalog sizes.
     */
    public static class CatalogItemSizeBase {
        public long widthMin;
        public long widthMax;

        public CatalogItemSizeBase() {
            this(0, 0);
        }

        public CatalogItemSizeBase(long widthMin, long widthMax) {
            this.widthMin = widthMin;
            this.widthMax = widthMax;
        }
    }

    /**
     * Catalog sizes and cardinality estimate.
     */
    public static class CatalogItemSize extends CatalogItemSizeBase {
        public final long cardinality;

        public CatalogItemSize(long cardinality) {
            super();
            this.cardinality = cardinality;
        }

        public CatalogItemSize(long widthMin, long widthMax, long cardinality) {
            super(widthMin, widthMax);
            this.cardinality = cardinality;
        }

        public long getItemCount() {
            return 1;
        }
    }

    public static class CatalogItemSizeRollup extends CatalogItemSize {
        public long itemCount;

        public CatalogItemSizeRollup(long cardinality, long itemCount) {
            super(cardinality);
            this.itemCount = itemCount;
        }

        @Override
        public long getItemCount() {
            return itemCount;
        }
    }

    /**
     * Raw index sizes.
     */
    public static class IndexSize extends CatalogItemSize {
        public final Index index;
        public final String name;

        public IndexSize(Index index, long widthMin, long widthMax, long cardinality) {
            super(widthMin, widthMax, cardinality);
            this.index = index;
            this.name = index.getTypeName().toLowerCase();
        }
    }

    /**
     * List of catalog item sizes with roll-up support.
     *
     * @param 
     */
    public static class CatalogItemSizeList extends ArrayList {
        private static final long serialVersionUID = -6846163291201059792L;

        public CatalogItemSizeRollup rollup(long cardinality) {
            CatalogItemSizeRollup rollupSize = new CatalogItemSizeRollup(cardinality, 0);
            for (T size: this) {
                rollupSize.widthMin  += (size.widthMin * size.cardinality);
                rollupSize.widthMax  += (size.widthMax * size.cardinality);
                rollupSize.itemCount += size.getItemCount();
            }
            return rollupSize;
        }
    }

    /**
     * Raw table sizes.
     */
    public static class TableSize extends CatalogItemSize {
        public final Table table;
        public final String name;
        public final boolean isView;
        public final CatalogItemSizeList indexSizes;

        public TableSize(
                Table table,
                boolean isView,
                long widthMin,
                long widthMax,
                long cardinality)
        {
            super(widthMin, widthMax, cardinality);
            this.table = table;
            this.name = table.getTypeName().toLowerCase();
            this.isView = isView;
            this.indexSizes = new CatalogItemSizeList();
        }

        public void addIndex(Index index, long widthMin, long widthMax) {
            this.indexSizes.add(new IndexSize(index, widthMin, widthMax, 1));
        }

        public CatalogItemSizeRollup indexRollup() {
            return this.indexSizes.rollup(this.cardinality);
        }
    }

    /**
     * Container of raw database size numbers.
     */
    public static class DatabaseSizes {
        public final CatalogItemSizeList tableSizes = new CatalogItemSizeList();
        public final CatalogItemSizeList viewSizes = new CatalogItemSizeList();
        public long indexCount = 0;

        public void addTable(TableSize tableSize) {
            if (tableSize.isView) {
                this.viewSizes.add(tableSize);
            }
            else {
                this.tableSizes.add(tableSize);
            }
            this.indexCount += tableSize.indexSizes.size();
        }

        public CatalogItemSizeRollup tableRollup() {
            return this.tableSizes.rollup(1);
        }

        public CatalogItemSizeRollup viewRollup() {
            return this.viewSizes.rollup(1);
        }

        public CatalogItemSizeRollup indexRollup() {
            CatalogItemSizeList indexSizes = new CatalogItemSizeList();
            for (TableSize tsize: this.tableSizes) {
                for (IndexSize isize: tsize.indexSizes) {
                    indexSizes.add(isize);
                }
            }
            for (TableSize vsize: this.viewSizes) {
                for (IndexSize isize: vsize.indexSizes) {
                    indexSizes.add(isize);
                }
            }
            return indexSizes.rollup(1);
        }
    }

    private static int roundedAllocationSize(int min, int contentSize) {
        int bufferSize = min;
        while (bufferSize < contentSize) {
            int increment = bufferSize / 2;
            bufferSize += increment;
            if (bufferSize >= contentSize) {
                break;
            }
            bufferSize += increment;
        }
        return bufferSize;
    }

    private static int getVariableColumnSize(VoltType type, int capacity, int dataSize, boolean forIndex, boolean isNullable) {
        assert(capacity >= 0);
        assert(dataSize >= 0);
        // Smaller capacities get fully consumed (plus 1 byte).
        if (capacity < 64) {
            return capacity + 1;
        }

        // Indexes get 8 byte pointers rather than replicate large data.
        if (forIndex) {
            if (type != VoltType.GEOGRAPHY) {
                return 8;
            }
            else {
                // Instances of GEOGRAPHY are not stored in indices.
                // Geospatial indexes decompose them into cells, which
                // are accounted for separately in the indexes that contain them.
                return 0;
            }
        }

        // For Nullable
        if (isNullable) {
            return 8;
        }

        // Larger capacities use pooled buffers sized in powers of 2 or values halfway
        // between powers of 2.
        // The rounded buffer size includes an object length of 4 bytes and
        // an 8-byte backpointer used in compaction.
        int content = 4 + 8 + dataSize;
        int bufferSize = roundedAllocationSize(8, content);
        // There is also has an 8-byte pointer in the tuple
        // and an 8-byte StringRef indirection pointer.
        return bufferSize + 8 + 8;
    }

    public static int testOnlyAllocationSizeForObject(int dataSize) {
        // See the comments in getVariableColumnSize for the significance of
        // these adjustments.
        int content = 4 + 8 + dataSize;
        if (content <= 48) {
            // Short-cut calculations for sizes that are not used in the
            // catalog sizing.
            return roundedAllocationSize(16, content);
        }
        // Otherwise exercise as much of the catalog sizing code path as
        // possible but strip out any adjustments that are not directly a
        // result of allocation rounding.
        // See the comments in getVariableColumnSize for the significance of
        // these adjustments.
        return getVariableColumnSize(VoltType.VARBINARY, 64, dataSize, false, false) - 8 - 8;
    }

    private static CatalogItemSizeBase getColumnsSize(List columns, boolean forIndex, boolean bAdjustForDrAA) {
        // See http://voltdb.com/docs/PlanningGuide/ChapMemoryRecs.php
        CatalogItemSizeBase csize = new CatalogItemSizeBase();
        for (Column column: columns) {
            VoltType ctype = VoltType.get((byte)column.getType());
            boolean isNullable = column.getNullable();
            if (ctype.isVariableLength()) {
                int capacity = column.getSize();

                if (ctype == VoltType.STRING && !column.getInbytes()) {
                    capacity *= MAX_BYTES_PER_UTF8_CHARACTER;
                }

                csize.widthMin += getVariableColumnSize(ctype, capacity, 0, forIndex, isNullable);
                csize.widthMax += getVariableColumnSize(ctype, capacity, capacity, forIndex, false);
            }
            else {
                // Fixed type - use the fixed size.
                csize.widthMin += ctype.getLengthInBytesForFixedTypes();
                csize.widthMax += ctype.getLengthInBytesForFixedTypes();
            }
        }

        //For DR active active enabled account for additional timestamp column for conflict detection.
        if (bAdjustForDrAA) {
            csize.widthMin += 8;
            csize.widthMax += 8;
        }
        return csize;
    }

    private static CatalogItemSizeBase getIndexSize(Index index) {

        // this is sizeof(CompactingMap::TreeNode), not counting template parameter KeyValuePair.
        final long TREE_MAP_ENTRY_OVERHEAD = 32;
        final long TUPLE_PTR_SIZE = 8;

        // All index types consume the space taken by the column data,
        // except that 8 byte pointers references replace large var... data.
        // Additional overhead is determined by the index type.
        CatalogMap columnRefsMap = index.getColumns();
        List indexColumns = new ArrayList(columnRefsMap.size());
        for (ColumnRef columnRef: columnRefsMap) {
            indexColumns.add(columnRef.getColumn());
        }
        //For index Size dont count the DR AA conflict column.
        CatalogItemSizeBase isize = getColumnsSize(indexColumns, true, false);
        if (index.getType() == IndexType.HASH_TABLE.getValue()) {
            // Hash index overhead follows this documented formula:
            //   w=column width, r=row count
            //      (((2 * r) + 1) * 8) + ((w + 32) * r)
            // This can be reduced to the following:
            //      (w + 48) * r + 8
            // For approximation purposes the constant +8 is ignorable.
            isize.widthMin += 48;
            isize.widthMax += 48;
        }
        else if (index.getType() == IndexType.COVERING_CELL_INDEX.getValue()) {
            // Covering cell indexes are implemented in the EE with two maps:
            //
            // [1 entry per table row] tuple address -> fixed-size array of 8 cell ids
            // [1-8 entries per table row]   cell id -> tuple address
            //
            // The polygon value is not referenced at all in the index, just the tuple address.
            // The call to getColumnsSize above purposely omits the size of the pointer to
            // the geography value for this reason.
            //
            // Other columns in the index are included, so if in the future we decide to support
            // multi-component geospatial indexes to optimize predicates like
            // "WHERE id = 10 and contains(geog, ?)", then this code would not need to change.

            final long MIN_CELLS = 1;
            final long MAX_CELLS = 8;
            final long CELL_SIZE = 8;
            final long TUPLE_MAP_ENTRY = TREE_MAP_ENTRY_OVERHEAD + TUPLE_PTR_SIZE + MAX_CELLS * CELL_SIZE;
            final long CELL_MAP_ENTRY = TREE_MAP_ENTRY_OVERHEAD + CELL_SIZE + TUPLE_PTR_SIZE;

            isize.widthMin += TUPLE_MAP_ENTRY + MIN_CELLS * CELL_MAP_ENTRY;
            isize.widthMax += TUPLE_MAP_ENTRY + MAX_CELLS * CELL_MAP_ENTRY;
        }
        else {
            // Tree indexes have a 40 byte overhead per row.
            isize.widthMin += TREE_MAP_ENTRY_OVERHEAD + TUPLE_PTR_SIZE;
            isize.widthMax += TREE_MAP_ENTRY_OVERHEAD + TUPLE_PTR_SIZE;
        }

        return isize;
    }

    private static TableSize getTableSize(Table table, boolean bActiveActiveEnabled) {
        // The cardinality is the estimated tuple count or an arbitrary number
        // if not estimated.
        long cardinality = table.getEstimatedtuplecount();
        if (cardinality <= 0) {
            cardinality = 1000;
        }
        //Do we need to adjust for DR-AA?
        boolean bAdjustForDrAA = (table.getIsdred() && bActiveActiveEnabled);

        // Add up the column widths.
        CatalogMap columnsMap = table.getColumns();
        List columns = new ArrayList(columnsMap.size());
        for (Column column: columnsMap) {
            columns.add(column);
        }
        CatalogItemSizeBase csize = getColumnsSize(columns, false, bAdjustForDrAA);

        boolean isView = table.getMaterializer() != null;
        TableSize tsize = new TableSize(table, isView, csize.widthMin, csize.widthMax, cardinality);

        // Add the table indexes.
        CatalogMap indexes = table.getIndexes();
        for (Index index: indexes) {
            CatalogItemSizeBase isize = getIndexSize(index);
            tsize.addIndex(index, isize.widthMin, isize.widthMax);
        }

        return tsize;
    }

    /**
     * Produce a sizing of all significant database objects.
     * @param dbCatalog  database catalog
     * @param isXDCR Is XDCR enabled
     * @return  database size result object tree
     */
    public static DatabaseSizes getCatalogSizes(Database dbCatalog, boolean isXDCR) {
        DatabaseSizes dbSizes = new DatabaseSizes();
        for (Table table: dbCatalog.getTables()) {
            dbSizes.addTable(getTableSize(table, isXDCR));
        }
        return dbSizes;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy