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

io.permazen.jsck.JsckInfo Maven / Gradle / Ivy

Go to download

Permazen analog to UNIX fsck(8) command for checking the consistency of, and repairing any corruption in, a Permazen key/value database

There is a newer version: 5.1.0
Show newest version

/*
 * Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
 */

package io.permazen.jsck;

import io.permazen.core.FieldType;
import io.permazen.core.type.EnumFieldType;
import io.permazen.core.type.ReferenceFieldType;
import io.permazen.kv.KVStore;
import io.permazen.schema.CounterSchemaField;
import io.permazen.schema.EnumSchemaField;
import io.permazen.schema.ListSchemaField;
import io.permazen.schema.MapSchemaField;
import io.permazen.schema.ReferenceSchemaField;
import io.permazen.schema.SchemaCompositeIndex;
import io.permazen.schema.SchemaField;
import io.permazen.schema.SchemaFieldSwitchAdapter;
import io.permazen.schema.SchemaModel;
import io.permazen.schema.SchemaObjectType;
import io.permazen.schema.SetSchemaField;
import io.permazen.schema.SimpleSchemaField;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;

/**
 * Runtime information used by {@link Jsck}.
 */
class JsckInfo implements JsckLogger {

    private final JsckConfig config;
    private final KVStore kv;
    private final AtomicLong counter = new AtomicLong();
    private final Map schemas = new HashMap<>();              // version -> SchemaModel
    private final Map> storages = new HashMap<>();   // version -> (storage ID -> Storage)
    private final Map indexes = new HashMap<>();                    // storage ID -> Index
    private final Consumer handler;

    private int formatVersion;

    JsckInfo(JsckConfig config, KVStore kv, Consumer handler) {
        this.config = config;
        this.kv = kv;
        this.handler = handler;
        if (this.config.getMaxIssues() <= 0)
            throw new MaxIssuesReachedException();
    }

    public JsckConfig getConfig() {
        return this.config;
    }

    public KVStore getKVStore() {
        return this.kv;
    }

    public Map getSchemas() {
        return this.schemas;
    }

    public Map> getStorages() {
        return this.storages;
    }

    public Map getIndexes() {
        return this.indexes;
    }

    public int getFormatVersion() {
        return this.formatVersion;
    }
    public void setFormatVersion(int formatVersion) {
        this.formatVersion = formatVersion;
    }

    // Handle an issue
    public void handle(Issue issue) {
        if (this.config.isRepair())
            issue.apply(this.kv);
        if (this.handler != null)
            this.handler.accept(issue);
        if (this.counter.incrementAndGet() >= this.config.getMaxIssues())
            throw new MaxIssuesReachedException();
    }

    public long getNumberOfIssuesHandled() {
        return this.counter.get();
    }

// JsckLogger

    @Override
    public boolean isDetailEnabled() {
        final JsckLogger logger = this.config.getJsckLogger();
        return logger != null && logger.isDetailEnabled();
    }

    @Override
    public void info(String message) {
        final JsckLogger logger = this.config.getJsckLogger();
        if (logger != null)
            logger.info(message);
    }

    @Override
    public void detail(String message) {
        final JsckLogger logger = this.config.getJsckLogger();
        if (logger != null && logger.isDetailEnabled())
            logger.detail(message);
    }

// Internal stuff

    // Inventory all storage ID's
    void inventoryStorages() {
        for (Map.Entry entry : this.schemas.entrySet())
            this.inventoryStorages(entry.getKey(), entry.getValue());
    }

    private void inventoryStorages(int schemaVersion, SchemaModel schema) {
        for (SchemaObjectType objectType : schema.getSchemaObjectTypes().values())
            this.inventoryStorages(schemaVersion, objectType);
    }

    private void inventoryStorages(final int schemaVersion, SchemaObjectType objectType) {

        // Add storage for object type
        this.addStorage(schemaVersion, new ObjectType(this, objectType));

        // Add storage for field indexes
        for (SchemaField field : objectType.getSchemaFields().values()) {
            field.visit(new SchemaFieldSwitchAdapter() {

                @Override
                public Void caseSimpleSchemaField(SimpleSchemaField field) {
                    if (field.isIndexed())
                        JsckInfo.this.addStorage(schemaVersion, new SimpleFieldIndex(JsckInfo.this, schemaVersion, field));
                    return null;
                }

                @Override
                public Void caseSetSchemaField(SetSchemaField field) {
                    if (field.getElementField().isIndexed())
                        JsckInfo.this.addStorage(schemaVersion, new SetElementIndex(JsckInfo.this, schemaVersion, field));
                    return null;
                }

                @Override
                public Void caseListSchemaField(ListSchemaField field) {
                    if (field.getElementField().isIndexed())
                        JsckInfo.this.addStorage(schemaVersion, new ListElementIndex(JsckInfo.this, schemaVersion, field));
                    return null;
                }

                @Override
                public Void caseMapSchemaField(MapSchemaField field) {
                    if (field.getKeyField().isIndexed())
                        JsckInfo.this.addStorage(schemaVersion, new MapKeyIndex(JsckInfo.this, schemaVersion, field));
                    if (field.getValueField().isIndexed())
                        JsckInfo.this.addStorage(schemaVersion, new MapValueIndex(JsckInfo.this, schemaVersion, field));
                    return null;
                }

                @Override
                public Void caseCounterSchemaField(CounterSchemaField field) {
                    return null;
                }
            });
        }

        // Add storage for composite indexes
        for (SchemaCompositeIndex index : objectType.getSchemaCompositeIndexes().values())
            this.addStorage(schemaVersion, new CompositeIndex(this, schemaVersion, objectType, index));
    }

    // Find FieldType for field
    FieldType findFieldType(final int schemaVersion, final SimpleSchemaField schemaField) {
        return schemaField.visit(new SchemaFieldSwitchAdapter>() {

            @Override
            public FieldType caseEnumSchemaField(EnumSchemaField field) {
                return new EnumFieldType(field.getIdentifiers());
            }

            @Override
            public FieldType caseReferenceSchemaField(ReferenceSchemaField field) {
                return new ReferenceFieldType(field.getObjectTypes());
            }

            @Override
            public FieldType caseSimpleSchemaField(SimpleSchemaField field) {
                final FieldType fieldType = JsckInfo.this.config.getFieldTypeRegistry().getFieldType(
                  field.getType(), field.getEncodingSignature());
                if (fieldType == null) {
                    throw new IllegalArgumentException("no FieldType named `" + field.getType() + "'"
                      + (field.getEncodingSignature() != 0 ? " with signature " + field.getEncodingSignature() : "")
                      + " (used by " + field + " in schema version " + schemaVersion
                      + ") was found in the configured FieldTypeRepository");
                }
                assert fieldType.getEncodingSignature() == field.getEncodingSignature();
                return fieldType;
            }
        });
    }

    // Add new Storage, checking for conflicts
    private void addStorage(final int schemaVersion, final Storage storage) {

        // Set schema version
        storage.setSchemaVersion(schemaVersion);

        // Double-check compatibility
        final int storageId = storage.getStorageId();
        for (Map map : this.storages.values()) {
            final Storage other = map.get(storageId);
            assert other == null || !(storage instanceof ObjectType) || !(other instanceof ObjectType);
            if (other != null && !storage.isCompatible(other)) {
                throw new IllegalArgumentException("schemas conflict for storage ID " + storageId
                  + ":\n  in schema version " + other.getSchemaVersion() + ": " + other
                  + ":\n  in schema version " + storage.getSchemaVersion() + ": " + storage);
            }
        }

        // Add storage
        this.storages.computeIfAbsent(schemaVersion, v -> new HashMap<>()).put(storageId, storage);

        // Add index
        if (storage instanceof Index)
            this.indexes.put(storageId, (Index)storage);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy