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

com.yahoo.vespa.hosted.controller.persistence.NotificationsSerializer Maven / Gradle / Ivy

There is a newer version: 8.253.3
Show newest version
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.persistence;

import com.yahoo.config.provision.ApplicationName;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.slime.Cursor;
import com.yahoo.slime.Inspector;
import com.yahoo.slime.Slime;
import com.yahoo.slime.SlimeUtils;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.notification.Notification;
import com.yahoo.vespa.hosted.controller.notification.NotificationSource;

import java.util.List;
import java.util.stream.Collectors;

/**
 * (de)serializes notifications for a tenant
 *
 * @author freva
 */
public class NotificationsSerializer {

    // WARNING: Since there are multiple servers in a ZooKeeper cluster and they upgrade one by one
    //          (and rewrite all nodes on startup), changes to the serialized format must be made
    //          such that what is serialized on version N+1 can be read by version N:
    //          - ADDING FIELDS: Always ok
    //          - REMOVING FIELDS: Stop reading the field first. Stop writing it on a later version.
    //          - CHANGING THE FORMAT OF A FIELD: Don't do it bro.

    private static final String notificationsFieldName = "notifications";
    private static final String atFieldName = "at";
    private static final String typeField = "type";
    private static final String levelField = "level";
    private static final String messagesField = "messages";
    private static final String applicationField = "application";
    private static final String instanceField = "instance";
    private static final String zoneField = "zone";
    private static final String clusterIdField = "clusterId";
    private static final String jobTypeField = "jobId";
    private static final String runNumberField = "runNumber";

    public static Slime toSlime(List notifications) {
        Slime slime = new Slime();
        Cursor notificationsArray = slime.setObject().setArray(notificationsFieldName);

        for (Notification notification : notifications) {
            Cursor notificationObject = notificationsArray.addObject();
            notificationObject.setLong(atFieldName, notification.at().toEpochMilli());
            notificationObject.setString(typeField, asString(notification.type()));
            notificationObject.setString(levelField, asString(notification.level()));
            Cursor messagesArray = notificationObject.setArray(messagesField);
            notification.messages().forEach(messagesArray::addString);

            notification.source().application().ifPresent(application -> notificationObject.setString(applicationField, application.value()));
            notification.source().instance().ifPresent(instance -> notificationObject.setString(instanceField, instance.value()));
            notification.source().zoneId().ifPresent(zoneId -> notificationObject.setString(zoneField, zoneId.value()));
            notification.source().clusterId().ifPresent(clusterId -> notificationObject.setString(clusterIdField, clusterId.value()));
            notification.source().jobType().ifPresent(jobType -> notificationObject.setString(jobTypeField, jobType.jobName()));
            notification.source().runNumber().ifPresent(runNumber -> notificationObject.setLong(runNumberField, runNumber));
        }

        return slime;
    }

    public static List fromSlime(TenantName tenantName, Slime slime) {
        return SlimeUtils.entriesStream(slime.get().field(notificationsFieldName))
                .map(inspector -> fromInspector(tenantName, inspector))
                .collect(Collectors.toUnmodifiableList());
    }

    private static Notification fromInspector(TenantName tenantName, Inspector inspector) {
        return new Notification(
                SlimeUtils.instant(inspector.field(atFieldName)),
                typeFrom(inspector.field(typeField)),
                levelFrom(inspector.field(levelField)),
                new NotificationSource(
                        tenantName,
                        SlimeUtils.optionalString(inspector.field(applicationField)).map(ApplicationName::from),
                        SlimeUtils.optionalString(inspector.field(instanceField)).map(InstanceName::from),
                        SlimeUtils.optionalString(inspector.field(zoneField)).map(ZoneId::from),
                        SlimeUtils.optionalString(inspector.field(clusterIdField)).map(ClusterSpec.Id::from),
                        SlimeUtils.optionalString(inspector.field(jobTypeField)).map(JobType::fromJobName),
                        SlimeUtils.optionalLong(inspector.field(runNumberField))),
                SlimeUtils.entriesStream(inspector.field(messagesField)).map(Inspector::asString).collect(Collectors.toUnmodifiableList()));
    }
    
    private static String asString(Notification.Type type) {
        switch (type) {
            case applicationPackage: return "applicationPackage";
            case deployment: return "deployment";
            case feedBlock: return "feedBlock";
            case reindex: return "reindex";
            default: throw new IllegalArgumentException("No serialization defined for notification type " + type);
        }
    }

    private static Notification.Type typeFrom(Inspector field) {
        switch (field.asString()) {
            case "applicationPackage": return Notification.Type.applicationPackage;
            case "deployment": return Notification.Type.deployment;
            case "feedBlock": return Notification.Type.feedBlock;
            case "reindex": return Notification.Type.reindex;
            default: throw new IllegalArgumentException("Unknown serialized notification type value '" + field.asString() + "'");
        }
    }

    private static String asString(Notification.Level level) {
        switch (level) {
            case info: return "info";
            case warning: return "warning";
            case error: return "error";
            default: throw new IllegalArgumentException("No serialization defined for notification level " + level);
        }
    }

    private static Notification.Level levelFrom(Inspector field) {
        switch (field.asString()) {
            case "info": return Notification.Level.info;
            case "warning": return Notification.Level.warning;
            case "error": return Notification.Level.error;
            default: throw new IllegalArgumentException("Unknown serialized notification level value '" + field.asString() + "'");
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy