postgres – btree level 1 not found in index

We have a jira instance which uses the postgres database to store its information. The server had roughly 50 Gb of disk, so it ran out of space before we noticed. We extended the partition to 100 Gb but apparently it was too late.
Jira started complaining about it with an 500 internal server error (Estado HTTP 500 – Could not determine database type. (Conexión rechazada. Verifique que el nombre del Host y el puerto sean correctos y que postmaster este aceptando conexiones TCP/IP.)). Viewing the logs we found Caused by: java.net.ConnectException: Connection refused

We checked our database and database settings for external connections and couldn’t connect.

Then we tried to start the postgres service

sudo service postgresql start

2018-12-04 09:01:59.268 CST [21281] LOG:  database system was interrupted while in recovery at 2018-12-04 08:59:20 CST
2018-12-04 09:01:59.268 CST [21281] HINT:  This probably means that some data is corrupted and you will have to use the last backup for recovery.
2018-12-04 09:01:59.631 CST [21281] LOG:  database system was not properly shut down; automatic recovery in progress
2018-12-04 09:01:59.636 CST [21281] LOG:  redo starts at 7/CC020C68
2018-12-04 09:01:59.639 CST [21281] LOG:  record with zero length at 7/CC07E998
2018-12-04 09:01:59.639 CST [21281] LOG:  redo done at 7/CC07E968
2018-12-04 09:01:59.639 CST [21281] LOG:  last completed transaction was at log time 2018-12-03 10:29:49.99078-06
2018-12-04 09:01:59.641 CST [21281] FATAL:  btree level 1 not found in index "19881"

VITALLY IMPORTANT FIRST RESPONSE

I did a backup of the postgres data files

ssh admin@jira.server
sudo tar cvf postgres.bkp.tar /var/lib/postgresql/

Copied it to my local machine

rsync -av admin@jira.server:postgres.bkp.tar .

then installed the same version (series) that is on the server into my local machine.
Since I have ubuntu 18.04 I had to add the postgres repository to install postgres version 9.3.

echo “deb http://apt.postgresql.org/pub/repos/apt/ bionic-pgdg main” | sudo tee /etc/apt/sources.list.d/pgdg.list
wget –quiet -O – https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add –
sudo apt update
sudo apt install postgresql-9.3 postgresql-contrib-9.3

Then I tried to run postgres locally with the backup data

cd /tmp
tar xvf postgres.bkp.tar
sudo su postgres
/usr/lib/postgresql/9.3/bin/postgres -D /tmp/var/lib/postgresql/9.3/main/

it complained about not finding the configuration file so I just copied it from the default dirs

cp /etc/postgresql/10/main/postgresql.conf /tmp/var/lib/postgresql/9.3/main/
cp -R /etc/postgresql/10/main/conf.d/ /tmp/postgres/var/lib/postgresql/9.3/main/

edit the postgresql.conf file and change the data dir appropiately

data_directory = ‘/tmp/var/lib/postgresql/9.3/main’

then I tried again (but this time in single mode) and it reproduced exactly the same error as on server. Great!

postgres@elite-tsj:/tmp$ /usr/lib/postgresql/9.3/bin/postgres --single -P -d 1 -D /tmp/var/lib/postgresql/9.3/main/
2018-12-04 09:01:59.268 CST [21281] LOG:  database system was interrupted while in recovery at 2018-12-04 08:59:20 CST
2018-12-04 09:01:59.268 CST [21281] HINT:  This probably means that some data is corrupted and you will have to use the last backup for recovery.
2018-12-04 09:01:59.631 CST [21281] DEBUG:  checkpoint record is at 7/CC025C50
2018-12-04 09:01:59.631 CST [21281] DEBUG:  redo record is at 7/CC020C68; shutdown FALSE
2018-12-04 09:01:59.631 CST [21281] DEBUG:  next transaction ID: 0/9547804; next OID: 227497
2018-12-04 09:01:59.631 CST [21281] DEBUG:  next MultiXactId: 3; next MultiXactOffset: 5
2018-12-04 09:01:59.631 CST [21281] DEBUG:  oldest unfrozen transaction ID: 676, in database 1
2018-12-04 09:01:59.631 CST [21281] DEBUG:  oldest MultiXactId: 1, in database 1
2018-12-04 09:01:59.631 CST [21281] DEBUG:  transaction ID wrap limit is 2147484323, limited by database with OID 1
2018-12-04 09:01:59.631 CST [21281] DEBUG:  MultiXactId wrap limit is 2147483648, limited by database with OID 1
2018-12-04 09:01:59.631 CST [21281] LOG:  database system was not properly shut down; automatic recovery in progress
2018-12-04 09:01:59.634 CST [21281] DEBUG:  resetting unlogged relations: cleanup 1 init 0
2018-12-04 09:01:59.636 CST [21281] LOG:  redo starts at 7/CC020C68
2018-12-04 09:01:59.639 CST [21281] LOG:  record with zero length at 7/CC07E998
2018-12-04 09:01:59.639 CST [21281] LOG:  redo done at 7/CC07E968
2018-12-04 09:01:59.639 CST [21281] LOG:  last completed transaction was at log time 2018-12-03 10:29:49.99078-06
2018-12-04 09:01:59.639 CST [21281] DEBUG:  resetting unlogged relations: cleanup 0 init 1
2018-12-04 09:01:59.641 CST [21281] FATAL:  btree level 1 not found in index "19881"

since this data is a copy i just executed pg_resetxlog as shown here.
Be aware that use of the pg_resetxlog utility should be done as an absolute last resort, and there are still some things you should try first..

/usr/lib/postgresql/9.3/bin/pg_resetxlog /tmp/var/lib/postgresql/9.3/main/

it complained I had to force it, there you go

/usr/lib/postgresql/9.3/bin/pg_resetxlog -f /tmp/var/lib/postgresql/9.3/main/

done! Now let’s see if it’s working again

sudo su postgres
/usr/lib/postgresql/9.3/bin/postgres -D /tmp/var/lib/postgresql/9.3/main/

boom! it’s working.

Now it’s time to do a backup with sql dump (pg_dump) or better yet a full sql dump (pg_dumpall).

In another terminal (or tab)

sudo su postgres
pg_dumpall -p 5433 > /tmp/postgres-full.dmp

in my case I had to specify the port (-p 5433).

Now that I have a full backup I can try the same on production.

/usr/lib/postgresql/9.3/bin/pg_resetxlog -f /var/lib/postgresql/9.3/main/
sudo service postgresql start

thank the gods! (to recall the option pointed here)

Anuncios

SmartGWT – Validación dependiente (dependant validation)

La validación de los datos de entrada es indispensable en cualquier aplicación. En las aplicaciones web tenemos que validar del lado del servidor y, es recomendable, validar del lado del cliente.

A continuación veremos como podemos validar del lado del cliente los campos de fecha de un formulario en SmartGWT.

En nuestro caso, tenemos un formulario sencillo donde se capturan dos fechas. Dado que estas fechas nos representan un rango, la fecha de fin siempre debe ser posterior a la fecha de inicio. En este caso se debe validar cuando cambie cualquiera de los dos campos. En SmartGWT lo podríamos realizar de la siguiente manera:

DynamicForm frmAudiencia = new DynamicForm();
DateTimeItem itmInicio = new DateTimeItem("inicio", "Inicio");
itmInicio.setType("datetime");
itmInicio.setShowPickerTimeItem(true);
itmInicio.setValidateOnChange(true);
DateTimeItem itmFin = new DateTimeItem("fin", "Fin");
itmFin.setType("datetime");
itmFin.setShowPickerTimeItem(true);
itmFin.setValidators(new CustomValidator() {
    {
        setDependentFields(new String[]{"inicio"});
    }
    @Override
    protected boolean condition(Object value) {
        return getRecord().getAttributeAsDate("inicio").before((Date) value);
    }
});
itmFin.setValidateOnChange(true);
frmAudiencia.setItems(itmNuc, itmTipoAudiencia, itmInicio, itmFin, itmJuez, itmSala);

De esta forma, cuando se modifique cualquiera de las dos fechas, se ejecuta el proceso de validación.

Fuentes:
http://smartclientexperience.wordpress.com/2011/10/19/introduction-to-smartclient-8-1smartgwt-2-5-validation-part-3/
http://www.smartclient.com/smartgwt/javadoc/com/smartgwt/client/widgets/form/validator/CustomValidator.html

Jackson JSON – Deserializar un array en una petición REST

En un servicio REST (jax-rs) puedes enviar los parámetros de una petición de varias formas: en la URL (@QueryParam), en el cuerpo del mensaje con el encabezado “application/x-www-form-urlencoded” (@FormParam), como parte de la URL (@PathParam).

Pero que pasa si el cliente envía toda la información en el cuerpo del mensaje en formato JSON.
Por ejemplo:

    {
        "tipo":"URGENTE",
        "actores":[
        {
            "nombre":"Juan",
            "apellidos":"Peréz",
            "idEstado":null,
            "idMunicipio":null,
            "idColonia":null,
            "calle":"Calle 2",
            "numero":"155",
            "codigoPostal":"76800"
        }
        ],
        "demandados":[
        {
            "nombre":"María",
            "apellidos":"López",
            "idEstado":null,
            "idMunicipio":null,
            "idColonia":null,
            "calle":"Calle 4",
            "numero":"45",
            "codigoPostal":"76800"
        }
        ]
    }

Podríamos crear una clase que reflejará esta estructura, pero si solo nos interesa algún campo o la petición no tiene nada que ver con nuestro modelo, no parece la mejor opción.

En este caso, en nuestro modelo existiría una clase Actor y Demandado que comparten los mismos campos, por lo que podríamos usar la herencia.

    public class Parte {
        String id;
        String nombre;
        String apellidos;
        String idEstado;
        String idMunicipio;
        String idColonia;
        String calle;
        String numero;
        String codigoPostal;
        public Parte() {
        }
        //... getter's y setter's, métodos, etc.
    }
    public class Actor extends Parte{
        //... otras propiedades
        public Actor() {
        }
        //... otros métodos
    }
    public class Demandado extends Parte{
        //... otras propiedades
        public Actor() {
        }
        //... otros métodos
    }

en nuestro servicio REST (Apache CXF) podríamos deserializar el arreglo de Actores de la siguiente forma

    @Path("solicitar/")
    public String solicitar(String data){
        String response = null;
        try{
            ObjectMapper mapper=new ObjectMapper();
            mapper.configure(SerializationConfig.Feature.WRITE<em>DATES</em>AS<em>TIMESTAMPS, false);
            mapper.configure(Feature.FAIL</em>ON<em>UNKNOWN</em>PROPERTIES, false);
            Map<String, Object> map = mapper.readValue(data, new TypeReference<Map<String, Object>>(){});
            String tipo = (String) map.get("tipo");
            List
<Parte> actores = mapper.convertValue(map.get("actores"), new TypeReference
<List<Parte>>() {});
            //... registrar la solicitud
            response = mapper.writeValueAsString(actores);
        }catch(Exception e){
            String error = "Ocurrió un problema al hacer la solicitud";
            log.error(error, e);
            response = String.format("{\"response\":{\"status\": -1, \"data\": \"%s\"}}", error);
        }
        return response;
    }

Fuentes:
http://wiki.fasterxml.com/JacksonDataBinding
http://cxf.apache.org/docs/jax-rs-basics.html#JAX-RSBasics-DealingwithParameters

SmartGwt – Actualizar los datos de un registro mediante código

Si tienes un ListGrid enlazado a un DataSource y quieres actualizar un registro mediante código, lo puedes hacer de la siguiente manera

</pre><p>IButton btnEliminar=new IButton("Eliminar");<br /> btnEliminar.addClickHandler(new ClickHandler() {<br /> <br /> @Override<br /> public void onClick(ClickEvent event) {<br /> final ListGridRecord empleado = grdEmpleados.getSelectedRecord();<br /> if(grdEmpleados.getSelectedRecord()!=null){<br /> SC.ask("Baja", "¿Dar de baja al empleado '"+empleado.getAttribute("nombre")+"' ?", new BooleanCallback() {<br /> <br /> @Override<br /> public void execute(Boolean eliminar) {<br /> if(eliminar){<br /> empleado.setAttribute("status", "BAJA");<br /> grdEmpleados.updateData(empleado);<br /> }<br /> }<br /> });<br /> }<br /> }<br /> });</p><pre><br />

En este ejemplo utilizamos un botón para dar de baja a un empleado, actualizando su propiedad status en vez de eliminar el registro.

Fuentes:

Changing the list grid values on runtime.

Java – Obtener el tipo de dato de las propiedades de un objeto

Para obtener el tipo de dato de las propiedades de un objeto podemos utilizar la reflexión.

Por ejemplo si tenemos las siguientes clases

public class Libro{
String isbn;
String titulo;
Autor autor;
}
public class Autor{
String id;
String nombre;
Date fechaNacimiento;
}

Un método para obtener el tipo de dato de una propiedad, incluso propiedades anidadas, sería el siguiente

private Class<?> getPropertyType(Class<?> clazz,String property){
 try{
 LinkedList<String> properties=new LinkedList<String>();
 properties.addAll(Arrays.asList(property.split("\\.")));
 Field field = null;
 while(!properties.isEmpty()){
 field = FieldUtils.getField(clazz,properties.removeFirst(),true);
 clazz=field.getType();
 }
 return field.getType();
 }catch(Exception e){
 throw new RuntimeException(e);
 }
}

Lo invocaríamos de la siguiente manera

Class<?> type=getPropertyType(Libro.class,"autor.fechaNacimiento");
System.out.println(type.toString()); //imprime java.util.Date

Notesé el uso de la clase FieldUtils de la librería Apache Commons Lang, cuyo método getField busca el campo recursivamente en la jerarquía de las clase y sus interfaces. Para agregar la librería a nuestro proyecto hay que agregar la siguiente dependencia


<dependency>
 <groupId>org.apache.commons</groupId>
 <artifactId>commons-lang3</artifactId>
 <version>3.1</version>
 </dependency>

Para listar todas las propiedades de un objeto recursivamente podemos utilizar como ejemplo el código de la clase FieldUtils.

Con la librería Jackson podemos obtener un esquema JSON de cualquier objeto. El detalle es que los tipos de datos que obtendríamos serían sólo los permitidos en JSON (string, number, boolean, object, array, null).

ObjectMapper mapper=new ObjectMapper();
System.out.println(mapper.generateJsonSchema(Libro.class));

Fuentes:
http://stackoverflow.com/questions/11125409/find-the-field-type-using-jackson-json
http://stackoverflow.com/questions/3567372/access-to-private-inherited-fields-via-reflection-in-java
http://wiki.fasterxml.com/JacksonJavaDocs
http://jackson.codehaus.org/1.8.8/javadoc/org/codehaus/jackson/map/ObjectMapper.html#generateJsonSchema(java.lang.Class)
http://wiki.fasterxml.com/JacksonTreeModel

BIRT – Conservar los espacios en blanco

En una consulta utilizo espacios para dar una sangría a los elementos, de acuerdo al nivel en que se encuentran.
Pues cuando creé un reporte -con BIRT– utilizando esa misma consulta, los registros me aparecían sin espacios.
Al parecer, BIRT remueve los espacios en blanco -de un DataItem- por default. Hace un especie de “Trim”.
Para evitar esto, tienes que modificar la propiedad “Format String” de la siguiente manera:

Format as: Custom
Format Code: ^

Simplemente poniendo el acento circunflejo hará que se mantengan los espacios en blanco.

Fuentes:
http://www.eclipse.org/forums/