class GossipDeviceStore extends AbstractStore<DeviceEvent, DeviceStoreDelegate> implements DeviceStore {

    @Override
    public synchronized List<DeviceEvent> updatePorts(ProviderId providerId,
                                                      DeviceId deviceId,
                                                      List<PortDescription> portDescriptions) {

        NodeId localNode = clusterService.getLocalNode().id();
        // TODO: It might be negligible, but this will have negative impact to topology discovery performance,
        // since it will trigger distributed store read.
        // Also, it'll probably be better if side-way communication happened on ConfigurationProvider, etc.
        // outside Device subsystem. so that we don't have to modify both Device and Link stores.
        // If we don't care much about topology performance, then it might be OK.
        NodeId deviceNode = mastershipService.getMasterFor(deviceId);

        // Process port update only if we're the master of the device,
        // otherwise signal the actual master.
        List<DeviceEvent> deviceEvents = null;
        if (localNode.equals(deviceNode)) {

            final Timestamp newTimestamp;
            try {
                newTimestamp = deviceClockService.getTimestamp(deviceId);
            } catch (IllegalStateException e) {
                log.info("Timestamp was not available for device {}", deviceId);
                log.debug("  discarding {}", portDescriptions);
                // Failed to generate timestamp.

                // Possible situation:
                //  Device connected and became master for short period of time,
                // but lost mastership before this instance had the chance to
                // retrieve term information.

                // Information dropped here is expected to be recoverable by
                // device probing after mastership change

                return Collections.emptyList();
            }
            log.debug("timestamp for {} {}", deviceId, newTimestamp);

            final Timestamped<List<PortDescription>> timestampedInput
                    = new Timestamped<>(portDescriptions, newTimestamp);
            final Timestamped<List<PortDescription>> merged;

            final Map<ProviderId, DeviceDescriptions> device = getOrCreateDeviceDescriptionsMap(deviceId);

            synchronized (device) {
                deviceEvents = updatePortsInternal(providerId, deviceId, timestampedInput);
                final DeviceDescriptions descs = device.get(providerId);
                List<PortDescription> mergedList =
                        FluentIterable.from(portDescriptions)
                                .transform(input ->
                                    // lookup merged port description
                                    descs.getPortDesc(input.portNumber()).value()
                                ).toList();
                merged = new Timestamped<>(mergedList, newTimestamp);
            }

            if (!deviceEvents.isEmpty()) {
                log.debug("Notifying peers of a ports update topology event for providerId: {} and deviceId: {}",
                         providerId, deviceId);
                notifyPeers(new InternalPortEvent(providerId, deviceId, merged));
            }

        } else {
            // Only forward for ConfigProvider
            // Forwarding was added as a workaround for ONOS-490
            if (!providerId.scheme().equals("cfg")) {
                return Collections.emptyList();
            }
            // FIXME Temporary hack for NPE (ONOS-1171).
            // Proper fix is to implement forwarding to master on ConfigProvider
            // redo ONOS-490
            if (deviceNode == null) {
                // silently ignore
                return Collections.emptyList();
            }

            PortInjectedEvent portInjectedEvent = new PortInjectedEvent(providerId, deviceId, portDescriptions);

            //TODO check unicast return value
            clusterCommunicator.unicast(portInjectedEvent, PORT_INJECTED, SERIALIZER::encode, deviceNode);
            /* error log:
            log.warn("Failed to process injected ports of device id: {} " +
                            "(cluster messaging failed: {})",
                    deviceId, e);
            */
        }

        return deviceEvents == null ? Collections.emptyList() : deviceEvents;
    }

}