Lo básico de Hadoop

Apache Hadoop es un framework opensource para escribir y ejecutar aplicaciones distribuidas que procesan grandes cantidades de datos.

El Objetivo es solucionar los problemas tradicionales de la computación a gran escala.

Las premisas sobre las que se asienta el framework son las siguientes:

  • Accesibilidad

Hadoop se puede ejecutar en grandes cluster de ordenadores o en servicios de cloud computing

  • Robustez

Tiene una gran tolerancia a fallos. Asume que es fácil que haya problemas de hardware debida a la gran cantidad de nodos sobre los que se puede ejecutar

  • Escalabilidad

Permite escalar horizontalmente mediante la inclusión de más nodos en el cluster

  • Simplicidad

Facililta el desarrollo de programas que se paralelicen sobre su arquitectura

Los dos pilares básicos: HDFS y MapReduce

  • HDFS es un sistema de ficheros distribuido sobre Hadoop con una alta tolerancia a fallos y diseñado para poder ser utilizado sobre hardware económico

HDFS

  • Map Reduce es un modelo para el procesamiento de datos a través de múltiples nodos

MapReduce

Cada nodo se encarga de procesar los datos almacenados en su nodo y ello se realiza en dos fases: Map y Reduce (optativa)

  • Map: En esta fase de mapeo se leen los datos en formato de clave/valor y produce una salida que puede ser nula u otras parejas de clave/valor

map(in_key, in_value) -> (inter_key, inter_value) list

Después de la fase Map los datos son combinados y ordenados (Shuffle and Sort)

  • Reduce: En esta fase se procesan los datos asegurándose que todos los datos con una misma clave se procesan en el mismo proceso Reduce y el resultado se escribe en el sistema de ficheros (HDFS)

Ejemplo del un proceso MapReduce para contar palabras

MapReduceWordCount2

 

Impala y Hive no tan parecidos

Etiquetas

,

Dos de los proyectos más usados para realizar consultas sobre el ecosistema Hadoop son Impala y Hive. Pero aunque a simple vista pueden parecer muy similares no lo son tanto.

Cloudera Impala es un motor distribuido de consultas sobre HDFS o HBase

  • Su arqutiectura es MPP (Massively Parallel Processing)
  • No usa el Map/Reduce de Hadoop sino que utiliza procesos que se ejecutan en los nodos y que consultan directamente sobre HDFS o HBase
  • Las consultas con Impala tienen una latencia menor que con Hive
  • Tiene un mayor consumo de memoria y de CPU que Hive

ArquitecturaImpala

Hive es un sistema de data warehouse sobre Hadoop

  • Su arquitectura se basa en el Map/Reduce de Hadoop
  • Es tolerante a fallos
  • Consultas más lentas que Impala pero más robustas

HiveArquitectura

Los ámbitos idóneos de aplicación de cada uno serían los siguientes:

Cloudera Impala aplicaría mejor en consultas más ligeras donde prime la velocidad sobre la fiabilidad, cualquier fallo en algún nodo o proceso obligaría a relanzar la consulta.

Hive es mejor para trabajos pesados de tipo ETL (Extract, Transform and Load) donde no nos interesa tanto la velocidad como la robustez de la ejecución, ya que la alta tolerancia a fallos que presenta evita la necesidad de relanzamientos al fallar algún nodo.

 

Persistir estados de Storm en MongoDB

Como ya hemos comentado Storm es un framework para procesar streams de forma distribuida basado en una topología previamente definida.

Trident nos facilita el procesado de los flujos de datos sobre Storm y permite persistir el resultado para poder recuperarlo posteriormente.

Ahora vamos a utilizar el ejemplo de Consultas DRPC con Trident para incorporarle lo necesario para guardar la cuenta de las palabras en MongoDB. Para ello vamos a utilizar 2 clases desarrolladas por Sjoerd Mulder: MongoState.java y  MongoValueResult.java

Incorporando a nuestro proyecto (pom.xml) TridentWordCountMongoDB.java las clases MongoState.javaMongoValueResult.java y Word.java de Sjoerd conseguimos la persistencia de los estados en MongoDB

TridentWordCountMongoDB

Ejecutando la clase la consola nos muestra::

Nº de ocurrencias de Es: [[8]]
Nº de ocurrencias de una: [[12]]
Nº de ocurrencias de novela: [[6]]
Nº de ocurrencias de primera:[[4]]

Y en MongoDB tenemos:

MongoDBResult

Consultas DRPC con Trident

Trident es una abstracción de alto nivel sobre Storm para facilitar el procesamiento de streams stateful.
Trident permite realizar joins, agregaciones, groups, funciones y filtros sobre los datos recibidos. Para saber más sobre Trident podéis mirar (entre otras mucha cosas) el API de Trident y el post un poco de Trident.

Storm permite ejecutar RPC (Llamadas a procedimientos remotos) de forma distribuida mediante la topología DRPC.

StormDRPC

Un ejemplo sobre como utilizar Trident para realizar consultas sobre el típico caso de la cuenta de palabras sería este:

  • Creamos el Spout para el test

TridentSpoutTest

  •  Creamos la topología para el contado de las palabras

TridentStatewordCounts

  • Creamos la topologia DRPC para las consultas del numero de ocurrencias de las palabras

TopologynewDRPCStream

  • Para realizar las consultas utilizamos el método execute del DRPC

Consultas

La clase completa TridentWordCount.java quedaría de la siguiente manera:

TridentWordCount

Y el resultado de la ejecución de la clase con las cuatro consultas nos muestra algo como esto:

ocurrencias

Cifrar correos en Gmail

Etiquetas

, ,

Son malos tiempos para la privacidad, es muy probable que cualquier mensaje de cualquier persona, organización, empresa o estado sea guardado por sistemas ajenos al receptor del mensaje por si “alguien” posteriormente considera oportuno consultarlo. Para conseguir la privacidad existen distintas soluciones la mayor parte de ellas requieren de ciertos conocimientos por parte de los usuarios, pero algunas  soluciones parciales nos permiten conseguir privacidad con muy poco esfuerzo.

Secure Gmail es una extensión de Gmail que permite cifrar correos electrónicos de forma sencilla (con un sólo click). Secure Gmail que te añade un botón con un candado al lado del de Redactar. Cuando quieras enviar un correo encriptado con una clave simétrica lo tienes que pulsar escribir el correo igual que siempre y enviarlo.

GmailSecureCandado

 

Al receptor del correo le llegará el correo cifrado con un enlace para desencriptarlo

encr

 

Agrupaciones de Stream en Storm

La topología en Storm es el mapa de Spouts y Bolts que muestran el flujo de los datos entre los distintos componentes de manera continua, realizando una analogía con Apache Hadoop la topología sería el MapReduce pero sin un fin en el tiempo.

StormTopologiaTuple

Uno de los temas más importantes a la hora de diseñar una topología es definir el intercambio de los datos entre los componentes. Las agrupaciones de Stream especifican como son consumidas las tuplas por cada Bolt y de que manera.

StormGrouping

Un stream en Storm es una secuencia ilimitada de tuplas.

StormStreamTuple

Estas son las distintas agrupaciones de stream que existen en Storm:

  • Shuffle Grouping

El envío de las tuplas a los bolts es aleatorio y uniformente. Sólo válido para operaciones atómicas

  • Local o Shuffle

Si el Bolt de destino tiene una o más tareas en el mismo proceso de trabajo las tuplas se envían preferentemente a esos workers

  • Fiels Grouping

El stream se divide por los campos especificados en la agrupación. Por ejemplo si un stream contiene un campo “user” forzaríamos que todas las tuplas con el mismo user irán a la misma tarea

  • All Grouping

Los stream son replicados atraves de todas las tareas de los bolts

  • Custom Grouping

Permite implementar una agrupación personalizada

  • Direct Grouping

El productor de la tupla decide qué tarea del consumidor recibirá esta tupla

  • Global Grouping

Envía todas las tuplas a un único destino

  • None Grouping

Esta agrupación es para indicar que no importa cómo se agrupa la corriente. En la práctica es similar a la Shuffle

Accediendo a Hive mediante JDBC

Etiquetas

, ,

  • Crear proyecto cliente JDBC Hive

mvn archetype:generate -DgroupId=test -DartifactId=hiveclientjdbc

  • Crear el pom.xml a utilizar con las dependencias necesarias

pom.xml

HiveClientJdbc

  • Cargar ficheros de datos en HDFS. Los ficheros que se utilizan se encuentran en el raíz de la distribución de Pivotal (/retail_demo)

./load_data_to_HDFS.sh

load_data_to_HDFS

  • Crear las tablas de Hive

hive -f create_hive_tables.sql

  • Cargar las tablas de Hive con los datos de los ficheros

hive -f loadDataHiveTables.sql

loadDataHieTables

  • Ejecutar el proyecto maven cliente

mvn compile exec:java -Dexec.mainClass=”test.HiveClientJdbc”

Ficheros utilizados:

pom.xml

  <modelVersion>4.0.0</modelVersion>
  <groupId>test</groupId>
  <artifactId>hiveclientjdbc</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>
  <name>hiveclientjdbc</name>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
    <repositories>
        <repository>
            <name>Cloudera repository</name>
            <id>cloudera-releases</id>
        </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>org.apache.hadoop.hive</groupId>
            <artifactId>hive-jdbc</artifactId>
            <version>0.7.1-cdh3u5</version>
            <type>jar</type>
            <exclusions>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop.hive</groupId>
            <artifactId>hive-metastore</artifactId>
            <version>0.7.1-cdh3u5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop.hive</groupId>
            <artifactId>hive-service</artifactId>
            <version>0.7.1-cdh3u5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop.hive</groupId>
            <artifactId>hive-exec</artifactId>
            <version>0.7.1-cdh3u5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-core</artifactId>
            <version>0.20.2</version>
        </dependency>
  </dependencies>
</project>

——————-

HiveClientJdbc.java

package test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class HiveClientJdbc {
    private static final String HIVE_DRIVER = “org.apache.hadoop.hive.jdbc.HiveDriver”;
    private static final String CONNECTION_URL = “jdbc:hive://localhost:10000/default”;
    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        Class.forName(HIVE_DRIVER);
        Connection con = DriverManager.getConnection(CONNECTION_URL, “”, “”);
        Statement stmt = con.createStatement();
        ResultSet rslt = stmt.executeQuery(“select count(*) from retail_demo.email_addresses_dim_hive”);
        if (rslt.next()) {
            System.out.println(rslt.getInt(1));
        }
        con.close();
    }
}

——————-

load_data_to_HDFS.sh

#!/bin/bash
base_dir=”/retail_demo”
# Clean up any previous load
echo “hadoop fs -rm -r -skipTrash $base_dir”
hadoop fs -rm -r -skipTrash $base_dir
echo “hadoop fs -mkdir $base_dir”
hadoop fs -mkdir $base_dir
for file in *.tsv.gz
do
  dir=`echo $file | perl -ne ‘s/^(.+?)\..+$/$1/;print;’`
  echo “hadoop fs -mkdir $base_dir/$dir”
  hadoop fs -mkdir $base_dir/$dir
  echo “hadoop fs -put $file $base_dir/$dir/”
  hadoop fs -put $file $base_dir/$dir/
done
——————-

create_hive_tables.sql

CREATE DATABASE IF NOT EXISTS retail_demo;
CREATE TABLE retail_demo.order_lineitems_hive
(
  Order_ID                      string
, Order_Item_ID                 bigint
, Product_ID                    int
, Product_Name                  string
, Customer_ID                   int
, Store_ID                      int
, Item_Shipment_Status_Code     string
, Order_Datetime                timestamp
, Ship_Datetime                 timestamp
, Item_Return_Datetime          timestamp
, Item_Refund_Datetime          timestamp
, Product_Category_ID           int
, Product_Category_Name         string
, Payment_Method_Code           string
, Tax_Amount                    double
, Item_Quantity                 int
, Item_Price                    double
, Discount_Amount               double
, Coupon_Code                   string
, Coupon_Amount                 double
, Ship_Address_Line1            string
, Ship_Address_Line2            string
, Ship_Address_Line3            string
, Ship_Address_City             string
, Ship_Address_State            string
, Ship_Address_Postal_Code      string
, Ship_Address_Country          string
, Ship_Phone_Number             string
, Ship_Customer_Name            string
, Ship_Customer_Email_Address   string
, Ordering_Session_ID           string
, Website_URL                   string
)
  ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘\t’
  STORED AS TEXTFILE
  LOCATION ‘/retail_demo/order_lineitems/’;
CREATE TABLE retail_demo.orders_hive
(
  Order_ID                      string
, Customer_ID                   int
, Store_ID                      int
— , Order_Datetime_Orig           timestamp
, Order_Datetime                timestamp
, Ship_Completion_Datetime      timestamp
, Return_Datetime               timestamp
, Refund_Datetime               timestamp
, Payment_Method_Code           string
, Total_Tax_Amount              double
, Total_Paid_Amount             double
, Total_Item_Quantity           int
, Total_Discount_Amount         double
, Coupon_Code                   string
, Coupon_Amount                 double
, Order_Canceled_Flag           string
, Has_Returned_Items_Flag       string
, Has_Refunded_Items_Flag       string
, Fraud_Code                    string
, Fraud_Resolution_Code         string
, Billing_Address_Line1         string
, Billing_Address_Line2         string
, Billing_Address_Line3         string
, Billing_Address_City          string
, Billing_Address_State         string
, Billing_Address_Postal_Code   string
, Billing_Address_Country       string
, Billing_Phone_Number          string
, Customer_Name                 string
, Customer_Email_Address        string
, Ordering_Session_ID           string
, Website_URL                   string
)
  ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘\t’
  STORED AS TEXTFILE
  LOCATION ‘/retail_demo/orders/’;
CREATE TABLE retail_demo.products_dim_hive
(
  Product_ID      int,
  Category_ID     smallint,
  Price           double,
  Product_Name    string
)
  ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘\t’
  STORED AS TEXTFILE
  LOCATION ‘/retail_demo/products_dim/’;
CREATE TABLE retail_demo.categories_dim_hive
(
  Category_ID    bigint,
  Category_Name  string
)
  ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘\t’
  STORED AS TEXTFILE
  LOCATION ‘/retail_demo/categories_dim/’;
CREATE TABLE retail_demo.email_addresses_dim_hive
(
  Customer_ID     int,
  Email_Address   string
)
  ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘\t’
  STORED AS TEXTFILE
  LOCATION ‘/retail_demo/email_addresses_dim/’;
CREATE TABLE retail_demo.date_dim_hive
(
  calendar_day      timestamp,
  reporting_year    smallint,
  reporting_quarter smallint,
  reporting_month   smallint,
  reporting_week    smallint,
  reporting_dow     smallint
)
  ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘\t’
  STORED AS TEXTFILE
  LOCATION ‘/retail_demo/date_dim/’;
CREATE TABLE retail_demo.customers_dim_hive
(
  Customer_ID    bigint,
  First_Name     string,
  Last_Name      string,
  Gender         string
)
  ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘\t’
  STORED AS TEXTFILE
  LOCATION ‘/retail_demo/customers_dim/’;
CREATE TABLE retail_demo.payment_methods_hive
(
  Payment_Method_ID    SMALLINT,
  Payment_Method_Code  string
)
  ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘\t’
  STORED AS TEXTFILE
  LOCATION ‘/retail_demo/payment_methods/’;
CREATE TABLE retail_demo.customer_addresses_dim_hive
(
  Customer_Address_ID  bigint,
  Customer_ID          bigint,
  Valid_From_Timestamp timestamp,
  Valid_To_Timestamp   timestamp,
  House_Number         string,
  Street_Name          string,
  Appt_Suite_No        string,
  City                 string,
  State_Code           string,
  Zip_Code             string,
  Zip_Plus_Four        string,
  Country              string,
  Phone_Number         string
)
  ROW FORMAT DELIMITED FIELDS TERMINATED BY ‘\t’
  STORED AS TEXTFILE
  LOCATION ‘/retail_demo/customer_addresses_dim/’;

——————-

loadDataHiveTables.sql

LOAD DATA LOCAL INPATH ‘/retail_demo/orders/orders.tsv.gz’ OVERWRITE INTO TABLE retail_demo.orders_hive;
LOAD DATA LOCAL INPATH ‘/retail_demo/products_dim/products_dim.tsv.gz’ OVERWRITE INTO TABLE retail_demo.products_dim_hive;
LOAD DATA LOCAL INPATH ‘/retail_demo/categories_dim/categories_dim.tsv.gz’ OVERWRITE INTO TABLE retail_demo.categories_dim_hive;
LOAD DATA LOCAL INPATH ‘/retail_demo/email_addresses_dim/email_addresses_dim.tsv.gz’ OVERWRITE INTO TABLE retail_demo.email_addresses_dim_hive;
LOAD DATA LOCAL INPATH ‘/retail_demo/date_dim/date_dim.tsv.gz’ OVERWRITE INTO TABLE retail_demo.date_dim_hive;
LOAD DATA LOCAL INPATH ‘/retail_demo/customers_dim/customers_dim.tsv.gz’ OVERWRITE INTO TABLE retail_demo.customers_dim_hive;
LOAD DATA LOCAL INPATH ‘/retail_demo/payment_methods/payment_methods.tsv.gz’ OVERWRITE INTO TABLE retail_demo.payment_methods_hive;
LOAD DATA LOCAL INPATH ‘/retail_demo/customer_addresses_dim/customer_addresses_dim.tsv.gz’ OVERWRITE INTO TABLE retail_demo.customer_addresses_dim_hive;

Apache Hive: SQL sobre Hadoop

Etiquetas

, , , , ,

Hive es un sistema de almacenamiento de datos de Hadoop que facilita el resumen de datos fácil, consultas ad-hoc, y el análisis de grandes conjuntos de datos almacenados en los sistemas de archivos compatibles Hadoop. Hive proporciona un mecanismo para la estructura de proyectar en estos datos y consultar los datos utilizando un lenguaje similar a SQL llamado HiveQL. Al mismo tiempo este lenguaje también permite mapa / reducir los programadores tradicional para enchufar en sus mapeadores y reductores personalizados cuando es inconveniente o ineficientes para expresar esta lógica en HiveQL.Hive

Arquitectura de Hive

Arquitectura Hive

Flujo de datos a través de Hive

Hive flujo de datos

El Modelo de datos de Hive se soporta sobre:

  • Tablas
  • Particiones en base a rangos
  • Buckets. Particiones Hash dentro de los rangos

Los tipos de datos soportados son:

Tipos primitivos::

  • Tinyint, smallint, int, bigint, float, boolean, double, string, binary y timestamp

Y los tipos complejos:

  • Array < primitive-type >, Map < primitive-type, data-type >, Struct < col-name : data-type, … > y UnionType

Metastore

Es el almacen que contiene definiciones de tablas y otros metadatos. Por defecto se  almacena de forma local en el equipo cliente en una base de datos Derby pero se puede configurar para que utilizar MySQL o cualquier otro servidor de base de datos relacional

Los programas que corren en mi Raspberry Pi

Etiquetas

Al final he sucumbido al influjo de la Raspberry Pi y me he comprado una. Como hay miles de proyectos y programas para la Raspberry Pi voy a comentar lo que a mi me han hecho tilín.

La distribución por la que he optado es la RaspBian, la ditribución media center para la Raspberry PI XBMC es una maravilla pero no cuadraba con mis intereses.

Mis programas imprescindibles:

SSH
Indudablemente el primer programa a instalar es el demonio SSH, un linux en red si este programa es un palomo cojo.

Openvpn
Mi siguiente necesidad es poder escaparde las jaulas informáticas que montan las empresas y como están últimanente se están espabilando lo mejor para salir de una forma sutíl es montar una vpn que escuche por el puerto 443 y para ello utilicé openvpn.

Squid
Quiero poder navegar por las todas las páginas de Internet sin las restricciones de los firewalls empresarialesasí que para ello he instalado un web proxy Squid al que accedo a través de la vpn.

Transmission
Lo más cómodo para mi para descargar vía torrent es Transmission que ofrece una interface web muy útil, también probé rtorrent y ctorrent con Screen pero no nos vamos a engañar Transmission es más usable.

Minidlna
Para el tema del media center he probado varias soluciones: omxplayer, XBMC… y al final lo mejor es comunicar todos los dispositivos de casa vía DLNA, utilizar la Raspberry PI para bajarse los ficheros y el reproductor de la tele para verlos. Gracias a las maravilla del DLNA todos los dispositivos comparten carpetas sin problemas así que puedes ejecutar los ficheros de unos y de otros.

Mpd
Cuando te llevas tu Raspberry de paseo y la quieres conectar al equipo de Pepito y escuchar tu música y no tienes un monitor a manolo mejor es MPD. En ese caso monto una red con mi móvil y la Raspberry Pi y manejo la música con el programa para Android DroidMPD, tienes clientes para MPD para todas las plataformas. Hay que configurar la Raspberry para que se ejecute el demonio del mpd en el arranque y para que se conecte a la red del móvil también en el arranque.

Omxplayer y Rasberry Remove
Si en nuestro paseo con la Raspberry también quieres ver una película y no quieres conectar un el teclado o ponerte a ejecutar comandos con la tele como monitor puedes montarte una la red con el móvil y utilizar el programa Rasberry Remove para lanzar la película desde el móvil y verla en la tele/monitor.

Servidor de ficheros
Para utilizar la Raspberry como servidor de ficheros me quedo con SSH y el cliente correspondiente frente a soluciones tipo nube XXX que aunque son más estéticas y sólo necesitas un navegador me parecen más engorrosas, mucho más pesadas y peores a la hora del mantenimiento.

The programs that run on my Raspberry Pi

Etiquetas

In the end i succumbed to the influence of the Raspberry Pi and i bought one.

As there are thousands of projects and programs for the Raspberry Pi i will discuss what i have done to my ding.

The distribution for which i chose is the RaspBian, the media center for ditribution Raspberry PI XBMC is great but did not fit with my interests.

My essential programs:

SSH

Undoubtedly the first program to install the SSH daemon is a network linux if this program is a lame pigeon.

Openvpn

My next need is to escaparde riding cages computer companies as they are improving lately are best to leave a subtle way is to set up a vpn to listen on port 443 and for this I used openvpn

Squid

I want to browse all websites without restrictions empresarialesasí firewalls that for this i installed a Squid web proxy that i am accessing via vpn.

Transmission

The easiest for me to download via torrent is Transmission web interface that offers a very useful and also tried rtorrent with Screen ctorrent but we’re going to cheat Transmission is more usable.

Minidlna

For media center theme I’ve tried several solutions: omxplayer, XBMC … and in the end it’s best to communicate all via DLNA home devices, use the Raspberry PI to download files and TV player to watch them. Thanks to the wonder of all DLNA devices seamlessly share folders so you can run the files of one or the other.

Mpd

When you bring your ride Raspberry and team want to connect John and listen to your music and not have a better manolo monitor MPD. In that case amount on my mobile network and the Raspberry Pi and managing music with the Android program DroidMPD, MPD have clients for all platforms. You have to set the Raspberry to run the mpd daemon on boot and connect to the mobile network also at startup.

Omxplayer and Rasberry Remote

If in our walk with Raspberry also want to see a movie and do not want to connect a keyboard or get to execute commands with the TV as a monitor can hop one with mobile network and use the program to launch movie Rasberry Remove from the mobile and see it on TV / monitor.

Fileserver Raspberry

To use as a file server and I keep SSH client facing solutions for cloud-XXX that are more aesthetic but only need a browser and seem more cumbersome, much heavier and worse at the time of maintenance.