PROJET AUTOBLOG


Planet-Libre

source: Planet-Libre

⇐ retour index

Journal du hacker : Liens intéressants Journal du hacker semaine #48

lundi 30 novembre 2020 à 00:01

Pour la 48ème semaine de l'année 2020, voici 10 liens intéressants que vous avez peut-être ratés, relayés par le Journal du hacker, votre source d’informations pour le Logiciel Libre francophone !

Pour ne plus rater aucun article de la communauté francophone, voici :

De plus le site web du Journal du hacker est « adaptatif (responsive) ». N’hésitez pas à le consulter depuis votre smartphone ou votre tablette !

Le Journal du hacker fonctionne de manière collaborative, grâce à la participation de ses membres. Rejoignez-nous pour proposer vos contenus à partager avec la communauté du Logiciel Libre francophone et faire connaître vos projets !

Et vous ? Qu’avez-vous pensé de ces articles ? N’hésitez pas à réagir directement dans les commentaires de l’article sur le Journal du hacker :)

Gravatar de Journal du hacker
Original post of Journal du hacker.Votez pour ce billet sur Planet Libre.

Articles similaires

Ulrich Van Den Hekke : Comment créer une bonne API Web - Partie 3

dimanche 29 novembre 2020 à 00:00

Bonjour,

Cet article fait partie d'un ensemble:

Il y a quelques années de cela, j'ai souhaité résoudre un problème que j'ai depuis longtemps avec les API REST: comment bien normaliser les tris, les projections, et les filtres. En effectuant mes recherches je suis tombé sur deux frameworks qui permettent de résoudre le problème des projections.

Qu'est qu'une API Falcor

Je ne vais parler que succinctement de Falcor. C'est un framework que je n'ai pas utilisé mais j'ai tout de même été très intéressé par ce dernier et je vais écrire quelques lignes sur ce Framework.

Pour plus d'informations, vous pourrez vous référer à la documentation.

Pour reprendre les explications de la documentation, Falcor est un middleware de votre application qui permet d'interroger des ressources au format JSON sur le serveur, comme votre application le ferait sur des données en mémoire.

Falcor

La requête envoyée alors au serveur ne contient que les champs demandés par le client, ce qui permet au serveur de sélectionner les champs et de ne retourner que ces champs. De plus le serveur peut n'exposer qu'un seul modèle contenant toutes les ressources (que ce soit pour retourner des listes ou des items particuliers).

En une seule requête, le client peut alors demander l'ensemble des données dont il a besoin, et l'afficher.

Nous n'avons plus alors le dilemne "faut-il créér une sous-resource, ou l'intégrer dans la ressource actuelle ?". Du point de vue du client: plus besoin d'effectuer plusieurs requêtes complexes pour récupérer plusieurs ressources, un seul appel suffit.

/model.json?paths=["user.name", "user.surname", "user.address"]

GET /model.json?paths=["user.name", "user.surname", "user.address"]
{
  user: {
    name: "Frank",
    surname: "Underwood",
    address: "1600 Pennsylvania Avenue, Washington, DC"
  }
}

Le Router falcor côté serveur, s'occupe alors de dispatcher les différentes parties demandées par le client à différents backends, qui peuvent alors répondre indépendament les uns des autres. Falcor va s'occuper alors d'aggréger le résultat.

Diagramme de service.

Pour récupérer des données depuis le front:

// ask for name and age of user with id = 5
model.get("users[5]['name','age']");// which will eventually return something like
{
  "users": {
    "5": {
      "name": "John Doe",
      "age": 33
    }
  }
}// you can also ask for ranges
model.get("users[5..7].name);// which eventually returns the following
{
  "users": {
    "5": { "name": "John Doe" },
    "6": { "name": "Jane Doe" },
    "7": { "name": "Mary Poppins" }
  }
}

L'avantage de ce framework, est qu'il permet de simplifier l'écriture des projections et de la partie lecture d'une API.

Ce pourquoi je n'ai pas choisi ce framework vient en deux choses:

Qu'est qu'une API GraphQL

Venant du constat que Falcor répondait à mon besoin de pouvoir normaliser la projection mais avec quelques limites, j'ai continué mes recherches et je suis tombé sur GraphQL.

GraphQL répond à la même problèmatique: pouvoir laisser au client choisir la projection qu'il souhaite des données. Comme Falcor, GraphQL permet de ramener plusieurs ressources en une requête. Et comme Falcor, GraphQL permet d'aggréger le résultat côté serveur de façon asynchrone.

L'avantage de GraphQL sur Falcor est que GraphQL est une norme écrite par Facebook alors que Falcor est une librairie Javascript. De la norme GraphQL, découle une implémentation officielle en Javascript mais aussi dans plein d'autres languages.

Par contre GraphQL n'est qu'un language de requête. Il ne décrit pas comment doit être transportée la requête sur le réseau, ni comment doit être transférée la réponse. Seul le contenu est normalisé. Les frameworks sont tout de même compatibles entre eux.

Par contre cela permet d'utiliser GraphQL pour autre chose que des requêtes réseaux. On pourrait envisager de faire un service qui ne répond qu'à des requêtes GraphQL. Ce service est alors rattaché au contrôleur pour une exposition mais aussi directement appelable en interne par d'autres services. Cela pourrait permettre de faire une couche d'abstraction interne.

Il existe plusieurs frameworks ajoutant la couche de transport à GraphQL. Dans la suite je parlerai d'une des implémentations qui se nomme Apollo.

Maintenant passons au vif du sujet.

Query - Introduction

Le système de requête de graphql permet au client de décrire ce qu'il souhaite récupérer. C'est au client de décider des éléments qu'il souhaite et de construire sa requête.

Voici un exemple de requête de requête:

query Dashboard {
  queueStats {
    waiting
    active
    failed
    lastExecution
    nextWakeup
  }
  diskUsageStats {
    currentRepartition {
      host
      total
    }
    currentSpace {
      size
      used
    }
  }
}

Derrière chaqu'un des membres ci-dessus, on peut retrouver un resolver. Un resolver c'est l'équivalent du contrôleur pour une API REST. C'est le resolver qui va récupérer les informations demandées par le client (arguments, projection, ...) et les transférer au service.

Il nous faudra alors faire un lien entre le schéma et ces resolvers. Pour le client, peu importe qu'il faille, pour une requête, exécuter en tâche de fond 1 resolver ou 10 resolver. Le client n'a pas besoin de le savoir.

Imaginons que dans l'exemple ci-dessous le champs nextWakeup nécessite un calcul complexe. Il suffit de créer un resolver qui effectue ce calcul. Si jamais le client n'a pas besoin de l'afficher et du coup, ne le demande pas, alors ce calcul complexe ne sera pas fait et c'est du temps de traitement gagné sur le serveur.

En REST, un champ qui nécessite un calcul complexe et soit

Nommer ces requêtes

Dans l'exemple ci-dessus, on peut remarquer que la requête possède un nom. C'est une bonne pratique de toujours nommer l'opération (ici Dashboard). Nommer les opérations permet de :

Query - Coté serveur

Côté serveur nous allons commencer par décrire le schéma que pourra alors utiliser le client pour effectuer ces requêtes. Par exemple :

scalar Date

"""
Define the state of the queue
"""
type QueueStats {
  """
  Number of task waiting in queue
  """
  waiting: Int!
  """
  Number of task active in queue
  """
  active: Int!
  """
  Number of task that have failed
  """
  failed: Int!
  """
  Date of the last execution
  """
  lastExecution: Date!
  """
  Date of the next wakeup
  """
  nextWakeup: Date!
}

type DiskCurrentRepartition {
  host: Int!
  total: Int!
}

type DiskCurrentSpace {
  size: Int!
  used: Int!
}

type DiskUsageStats {
  currentRepartition: [DiskCurrentRepartition!]!
  currentSpace: DiskCurrentSpace!
}

type Query {
  queueStats: QueueStats!
  diskUsageState: DiskUsageStats!
}

On peut remarquer plusieurs choses:

Ce schéma (qui peut aussi être généré à l'aide d'annotations en Typescript, par exemple) sert également de documentation (un peu comme swagger).

Il est alors possible d'utiliser des outils comme

pour générer de la documentation mais aussi des outils comme

Une fois le schéma écrit, il peut être communiqué aux équipes front (si les équipes sont séparées). Pendant que l'on développe alors le front, côté serveur on peut alors implémenter le schéma.

Pour implémenter le schéma ci-dessus, nous allons écrire un resolver (en Javascript pour l'exemple).

const resolvers = {
  Query: {
    queueStats() {
      return getQueueStats();
    },
    async diskUsageState() {
      return await getDiskUsageState();
    },
  },
};

Un resolver peut pour un champ:

Ainsi imaginons que la méthode getQueueStats() retourne l'objet suivant, dans lequel il manque nextWakeup et lastExecution :

{
  "waiting": 0,
  "active": 2,
  "failed": 0
}

Il est possible d'écrire un resolver :

const resolvers = {
  Query: {
    queueStats() {
      return getQueueStats();
    },
    async diskUsageState() {
      return await getDiskUsageState();
    },
  },
  QueueStats: {
    lastExecution(parent /*: QueueStats */) {
      const { active } = parent; // Ici pour l'exemple on peut récuperer un attribut de parent.
      return getLastExecution();
    },
    nextWakeup() {
      return getNextWakup();
    },
  },
};

Il est alors possible de créer son schéma en pensant à comment le client va intéroger ce dernier, et lors de l'implémentation ajouter des structures complexes qui sont issues du calcul synchrone ou asynchrone des données.

Query - Nullable

On a vu précédement qu'on pouvait utiliser le point d'exclamation pour indiquer qu'un champ ne sera jamais NULL. Cela peut avoir des avantages pour le client mais cela a aussi de grandes implications côté serveur.

Par défaut pour GraphQL, tous les champs sont nullable par défaut. En effet, si un resolver n'arrive pas à récupérer la donnée (erreur côté serveur, back HS, problème de base de données, problème réseau, droits d'accès différents selon les champs ...), le serveur pourra retourner NULL à la place de la valeur (et une erreur en parallèle du json). Cela permet au client une vue partielle même si certains services ne sont pas disponibles.

Si le champ ne peut pas être null, alors le json ne pourra pas du tout être envoyé et c'est la requête complète qui est en ereur.

C'est pour cela qu'en GraphQL chaque champ peut, par défaut, obtenir la valeur null.

Lors de la conception d'un schéma GraphQL, il faut utiliser la possibilité de rendre le champ non nullable avec réflexion et uniquement pour les champs dont on souhaite garantir la non nullité.

Query - Ajout d'arguments

query HeroNameAndFriends($episode: Episode) {
  hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}

Il est possible en GraphQL de définir certain champs comme ayant des paramètres. Ils seront alors passés au resolver. Il est également possible d'avoir des paramètres à différents niveaux du schéma (pas seulement au niveau le plus haut).

Le passage de paramètres permet d'écrire son opération une fois et ensuite de l'appeler avec des paramètres. C'est important de définir les saisie utilisateurs comme des paramètres pour éviter les injections GraphQL (comme en SQL, ou autre).

Règle n°1: Ne jamais faire confiance à l'utilisateur.

Query - Création d'alias

Si on souhaite récupérer plusieurs valeurs d'un attribut en fonction de ses paramètres, il est possible de le demander plusieurs fois et de lui associer un alias.

query aliasQuery {
  empireHero: hero(episode: EMPIRE) {
    name
  }
  jediHero: hero(episode: JEDI) {
    name
  }
}

Dans le JSON résultant, on retrouve alors les deux attributs empireHero et jediHero qui sont tous les deux issus du membre hero avec un paramètre différent. Cela peut aussi être utilisé pour simplement renommer un champ.

Query - Fragment

Les fragments permettent de factoriser et de créer des morceaux de requêtes réutilisables.

# Dans le fichier FragmentJob.graphql

fragment FragmentJob on Job {
  id
  state
  failedReason
  data {
    host
  }
}

# Dans le fichier QueueTasks.graphql

#import "./FragmentJob.graphql"

query QueueTasks($state: [String!]) {
  queue(state: $state) {
    ...FragmentJob
  }
}

Le Fragment FragmentJob peut alors être réutilisé dans différentes requêtes, voir même plusieurs fois dans la même requête.

Query - Gestion de version

J'en parlais dans les articles précédents, il existe plusieurs manières de versionner une API. GraphQL n'y échappe pas. On peut:

Les créateurs de GraphQL partagent leur opinions. Comme c'est le client qui décide des champs qu'il souhaite rappatrier, toute évolution du schéma ajoutant de nouveaux attributs ne pose aucun problème et ne surchargera pas plus le client. Les suppressions doivent passer par une phase d'obsolescence ou les attributs sont marqués avec l'annotation @deprecated et leur décommissionnement automatique.

Afin de pouvoir facilement décommissionner des attributs, le mieux est de savoir quels attributs sont utilisés ou non. La librairie Apollo permet de se connecter au studio d'appolo et de visualiser l'utilisation des attributs. Savoir qu'un attribut n'est pas utilisé permet de le décommissionner ou de le modifier. Bien sûr et malheureusement cette partie n'est pas open source. Je n'ai pas encore trouvé de dashboard OpenSource permettant d'analyser l'utilisation d'un champ.

Ce qui est important c'est d'avoir une politique de versionning.

Query - Pagination

GraphQL n'a pas défini de règle concernant la pagination car il y a plein de manières de la gérer. Pour des listes contenant peu d'éléments, il n'y a par exemple pas lieu de gérer la pagination.

Quand on envisage la pagination, on peut en faire une par page avec un nombre d'éléments à passer (skip), ou une basée sur l'id du premier élément à afficher.

Il existe également des patterns que l'on peut utiliser pour ne pas réinventer une nouvelle facon de faire.

Par exemple l'un de ses patterns s'appele Connections et des librairies comme Relay savent comment gérer automatiquement ce pattern.

Dans le pattern Connections, nous retrouvons les notions:

{
  hero {
    name
    friendsConnection(first: 2, after: "Y3Vyc29yMQ==") {
      totalCount
      edges {
        node {
          name
        }
        cursor
      }
      pageInfo {
        endCursor
        hasNextPage
      }
    }
  }
}

On peut retrouver la spécification de ce modèle de pagination sur le site de relay.dev

Query - Cache - Control

Contrairement au REST où on peut peut utiliser les header HTTP, pour contrôler le cache, en GraphQL une requête peut avoir des limites d'âge différentes sur les différentes resources appelées. Il faut donc trouver d'autres moyens de gérer le cache.

C'est pour moi un des points les plus gênants de GraphQL.

Sur le serveur, il y a plusieurs manières complémentaires (à différentes fins) de gérer le cache. Nous allons nous concentrer sur la librairie apollo-server.

La 1ère manière s'apparentant au cache-control des requêtes HTTP peut être utilisée avec la directive @cacheControl ou au niveau du resolver. Elle ne permet pas de cacher directement la donnée mais de donner une indication au client sur combien de temps cette donnée peut-être cachée. Si la librairie utilisée côté client est également apollo elle utilisera alors ces indications pour gérer le cache de la donnée (et donc ne pas ré-effectuer la requête.)

scalar Date

"""
Define the state of the queue
"""
type QueueStats @cacheControl(maxAge: 60) {
  """
  Number of task waiting in queue
  """
  waiting: Int!
  """
  Number of task active in queue
  """
  active: Int!
  """
  Number of task that have failed
  """
  failed: Int!
  """
  Date of the last execution
  """
  lastExecution: Date! @cacheControl(maxAge: 3600)
  """
  Date of the next wakeup
  """
  nextWakeup: Date! @cacheControl(maxAge: 3600)
}

Je vous invite à lire la documentation d'apollo qui contient énormément d'informations. Cette méthode permet d'ajouter des header http pour cacher les requêtes dans un CDN intérmédiaire, ou dans une base redis attachée au serveur, ou dans le cache navigateur, voir également au niveau même du client.

Query - Cache - DataLoader

La 2nd méthode de cache consiste à l'utilisation de DataLoader. L'ajout d'un dataloader a pour but de cacher le temps d'une requête les différents éléments pour éviter de multiplier les appels. Le but n'étant pas de réellement faire du cache mais plutôt de pouvoir traiter une requête batch le plus rapidement possible.

Imaginons par exemple la requête suivante :

query TestDataLoader {
  backups {
    id
    startDate
    endDate
    user {
      firstName
      lastName
    }
  }
}

Dans le cas présent, on souhaite récupérer les backups, mais également pour chaque backup récupérer l'utilisateur. Si l'utilisateur est le même pour chaque backup, on se retouve alors à charger l'utilisateur plusieurs fois. Le fait d'utiliser un dataloader permettra dans le cas présent de ne les charger qu'une fois.

Pour utiliser le système de data loader, on peut utiliser la bibliothèque du même nom: https://github.com/graphql/dataloader. Il nous faut alors créer un dataloader pour un type d'objet.

import DataLoader from "dataloader";

const userLoader = new DataLoader((keys) => service.getUsers(keys));

Ensuite dans les resolvers, au lieu de récupérer les utilisateurs directement à partir du service, on utilise le data loader pour récupérer la valeur :

const resolvers = {
  async backups() {
    return service.find();
  },
  Backup: {
    async user(ctx, { id }) {
      return await userLoader.load(id);
    },
  },
};

Dataloader n'est par contre pas fait pour être utilisé en tant que cache applicatif et ne remplace donc pas un memcached ou un redis.

Query - Batch

Apollo côté client propose de pouvoir faire du batching de requêtes. Cela consiste à attendre un léger laps de temps pour regrouper en une seule requête plusieurs requêtes.

Attention néanmoins, mettre en place le batching de requêtes implique:

Il est alors conseillé d'abord de faire d'autres types d'optimisations (comme utiliser les requêtes persistentes, un cache dans un CDN des réponses, utiliser HTTP/2 jusqu'au NodeJS) avant de mettre en place le système de batch.

Si vous voulez le mettre tout de même en place, vous pouvez regarder la documentation d'Apollo sur ce sujet.

Query - Transfert réseau

Pour une API Web, les requêtes et les réponses sont transférées en utilisant le protocole HTTP. Pour améliorer les performances il est possible d'utiliser plusieurs méthodes:

Mutation

Nous avons énormément parlé de toute la partie requête de GraphQL. GraphQL permet également de faire des modifications via des mutations.

Une mutation est similaire à une requête :

mutation CreateBackup($backup: CreateBackupInput!) {
  createBackup(backup: $backup) {
    statusCode
    message
    error
    errorCode
    backup {
      id
      state
      user {
        id
      }
    }
  }

Nous retrouvons la partie input, et la partie query (sur la réponse). Alors que généralement sur query on va retrouver des types primitifs en paramètres, on va plutôt retrouver en paramètre d'une mutation un objet de type Input. Sur le résultat le fonctionnement est lui le même que sur une Query.

Nous pouvons d'ailleurs utiliser les mêmes resolvers sur les réponses que dans les queries (et ainsi récupérer l'utilisateur de la backup de la même manière).

En bonne pratique, ce que nous pouvons retrouver sur les mutations sont:

Il ne faut pas hésiter à découper ces actions en petites actions que l'utilisateur peut choisir d'appeler ou non (il est possible d'appeler plusieurs mutations en un seul appel).

Si on reprend le schéma suivant:

input InputUser {
  name: String!
}

input CreateBackupInput {
  host: String!
  user: InputUser!
}

type Backup {
  host: String!
  id: Int!
  startDate: Date
  endDate: Date
  user: User
}

type User {
  name: String
  backups: [Backup!]
}

type CreateBackupResponse {
  statusCode: Int!
  message: String
  error: String
  errorCode: String
  backup: Backup
}

Contrairement aux paramètres de sortie, il n'est pas possible sur les input de faire des dépendances circulaires. Un input doit donc être lié à l'action faite par la mutation. Cela veux dire aussi qu'il n'est pas possible de réutiliser un type de sortie en input pour l'entrée. Le point d'exclamation n'est plus un indicateur de non nullité mais un indicateur de paramètre obligatoire.

Lors de la conception des ces inputs il faut d'ailleurs penser à plusieurs choses:

Pour une application possédant beaucoup de formulaires et d'écrans de modifications ou d'actions, il peut être donc compliqué d'écrire les requêtes et les mutations associées.

Il peut peut-être être intéressant d'utiliser un transpiler comme graphql-s2s qui permet de simplifier certaines choses dans l'écriture du schéma. Lors de son utilisation sur un projet possédant un gros schéma, il a permis la reprise d'une API Rest existante plus facilement.

Il permet entre autres:

Subscription

Un des points que j'adore avec GraphQL c'est la facilité d'implémentation qu'apporte Apollo avec les Subscritpions. Quand on souhaite remonter des informations du serveur au client, on peut mettre en place des WebSockets ou des Server-Side-Events. Il faut alors définir un potocole entre le client et le serveur.

GraphQL utilise de façon transparente une WebSocket dans le cadre des souscriptions. L'avantage c'est que le protocole est le même que pour les query. On effectue une demande et au lieu d'avoir une réponse, une fois, on reçois les mise à jours sur la souscription.

Cela permet de mettre en place facilement des pages dynamiques qui évoluent sans actions utilisateurs (progression, chat, ...) sans se casser la tête.

Pour définir une souscription on peut utiliser une requête comme celle-ci:

#import "./FragmentJob.graphql"

subscription QueueTasksJobUpdated {
  jobUpdated {
    ...FragmentJob
  }
}

Dans l'exemple ci-dessous je réutilise le fragment d'un job défini dans une query. La souscription dans apollo va automatiquement mettre à jour le résultat et rafraichir l'IHM.

Par exemple avec vue cela donne (cf la doc de vue-apollo):

apollo: {
  tags: {
    query: TAGS_QUERY,
    subscribeToMore: {
      document: gql`subscription name($param: String!) {
        itemAdded(param: $param) {
          id
          label
        }
      }`,
      // Variables passed to the subscription. Since we're using a function,
      // they are reactive
      variables () {
        return {
          param: this.param,
        }
      },
      // Mutate the previous result
      updateQuery: (previousResult, { subscriptionData }) => {
        // Here, return the new result from the previous with the new data
      },
    }
  }
}

La requête initiale est faite grâce à TAG_QUERY, puis toutes les mises à jours se font via la souscription. L'IHM est alors automatiquement mise à jour.

Conclusion

GraphQL est super puissant et facilite l'écriture d'API et son utilisation par des clients. Par contre la montée en compétence pour faire du GraphQL est un peu plus élevée que pour faire du REST (Bien qu'il n'est pas facile de faire du bon REST).

Il peut toujours être utile de proposer des API Rest en plus des API GraphQL. Pour par exemple permettre au client de choisir ce qu'il souhaite utiliser ou même par exemple pour des cas d'usage particuliers. (Par exemple le téléchargement/l'upload d'un document binaire est plus facile en REST que en GraphQL).

Références

Gravatar de Ulrich Van Den Hekke
Original post of Ulrich Van Den Hekke.Votez pour ce billet sur Planet Libre.

Articles similaires

Kiddo : Les cônes oranges du libre: du ramollissement de l’enthousiasme face aux chantiers technologiques

jeudi 26 novembre 2020 à 18:25

Dans ce billet, l’ami “Antistress” (Thibaut) fait un état des lieux des grands changements technologiques du libre autour de la plateforme GNU+Linux depuis une quinzaine d’années, lorsque plusieurs d’entre nous ont commencé à utiliser cette plateforme plus intensivement. Il y laisse également transpirer un certain épuisement, ou “manque d’excitation”, qui est probablement ressenti par plusieurs d’entre nous.

Pour ma part j’ai commencé à bidouiller avec Linux en 2003, mais ce n’est qu’avec la première version d’Ubuntu, 4.10, que j’ai pu y coller à plein temps (note: je suis passé à Fedora depuis 2010). Et depuis, ça a été une histoire de patience. Beaucoup, beaucoup de patience.

Je vois souvent des parallèles entre le monde du logiciel libre et la ville de Montréal. Comme les chantiers dans les rues de Montréal qui semblent éternels (au point où nous avons un cône orange qui nous sert de mascotte informelle), on a l’impression de ne pas voir le bout de nos chantiers technologiques.

Les cônes oranges typiques du paysage montréalais, une photo du Devoir (pour mes besoins de parodie)

La tâche de modernisation technologique de notre système d’opération favori est colossale, a impliqué nombre d’individus et de compagnies (qui sont venues et parties à divers moments des “desktop wars” et “mobile wars”), et nombres de rebondissements et apprentissages en cours de route. On peut penser notamment:

Dans le monde du libre, nous sommes tous un peu sado-masos. Masochistes pour ce qui est de la patience dont nous devons faire preuve, et sadiques dans notre relation amour-haine envers nos logiciels favoris (je ne me suis pas gêné, par le passé, pour être assez critique sur mon blog concernant les performances de Firefox; critiques qui sont, une décennie plus tard, enfin résolues), de la même façon que les Montréalais(e)s ont une relation amour-haine avec les cônes oranges (même sans être fans de VLC). Et c’est sans compter la déprimante hégémonie de Google et Apple dans le monde du mobile au sein de la population, ce qui confine notre impact au monde du “desktop”.

Alors effectivement, après toutes ces années, on devient un peu moins excités que dans notre jeunesse où on se faisait fervents évangélistes du libre. Thibaut est assez clair lorsqu’il écrit ceci:

[…] je dirais qu’à part pour le premier item (la finalisation des chantiers en cours de Firefox sous GNU/Linux), je manque un peu d’excitation.

Pour expliquer ce phénomène, je crois que je dirais ceci: du côté “desktop Linux” il y a eu beaucoup de progrès un peu partout au fil des années, et une des raisons pour laquelle nous sommes peut-être moins excités par rapport aux nouveautés sur nos ordinateurs, c’est aussi parce que “globalement, ça marche maintenant” (vous souvenez-vous de l’époque pré-Xorg où on devait s’attendre à ce que l’interface graphique ne fonctionne pas au premier démarrage? et l’époque 2004-2012 où on devait se battre pour avoir du WiFi qui fonctionne? Heureusement je n’ai plus croisé de Broadcom dans les dernières années…).

Donc, il ne reste plus tant de choses criantes que ça à régler. Et ce qui reste à régler… remplace souvent des choses qui “fonctionnent”. De 2003 à 2013 environ, j’ai eu plusieurs situations où mon système de fichiers ext4 s’est spontanément corrompu “par lui-même”, sans explication (alors que ext4 était supposément stable et largement testé depuis des années). Depuis 2013-2014, ça ne m’est plus arrivé. Alors, pour reprendre le constat d’Antistress concernant l’adoption modeste de Btrfs, excusez-moi, mais je n’ai aucun intérêt ni enthousiasme à reformatter mon disque dur 2 teraoctets (détenant mon /home) de ext4 vers Btrfs. Ni l’empressement de passer à Fedora Silverblue au lieu de Fedora Workstation. Je vais laisser les nouveaux jeunes s’amuser avec ça!

Gravatar de Kiddo
Original post of Kiddo.Votez pour ce billet sur Planet Libre.

Articles similaires

Guillaume Kulakowski : TTL pour les DNS locaux sur OpenWRT

jeudi 26 novembre 2020 à 13:43

Récemment, je me suis un peu amusé avec AdGuard Home sur mon routeur OpenWRT. J’y ai rapidement constaté que mon serveur NAS ainsi que ma box domotique faisaient énormément d’appels au DNS. En effet, avec l’utilisation systématique de baux statiques et de noms de domaine locaux, j’ai remplacé les IPs de certain composant par leur […]

Cet article TTL pour les DNS locaux sur OpenWRT est apparu en premier sur Guillaume Kulakowski's blog.

Gravatar de Guillaume Kulakowski
Original post of Guillaume Kulakowski.Votez pour ce billet sur Planet Libre.

antistress : Ma configuration de Firefox en cette fin 2020

mercredi 25 novembre 2020 à 23:37

Panda roux à l'affut
Firefox se purgeant

Occasionnellement je fais une remise à zéro de mon panda roux, l'occasion de ré-évaluer ce que je garde et ce que je jette.

Voilà ma configuration en cette fin novembre, tandis que je roule la version 83.

Certaines modifications concernent les fonctionnalités, d'autres la vie privée, d'autres encore la sécurité ou les performances.

Réglages basiques via les Préférences

Après une remise à zéro (fonction « Réparer Firefox… » de la page about:support), je cours cocher « Restaurer la session précédente » dans les Préférences / section Général pour ne pas perdre tous mes onglets au redémarrage.

Plus anecdotique, au titre des raccourcis clavier pour naviguer dans les onglets, dans les Préférences / onglet Général / section Onglets, je décoche « Ctrl+Tab fait défiler vos onglets en les classant selon leur dernière utilisation ». Ce comportement a été changé récemment, et je préfère l'ancien comportement qui me permet de naviguer au clavier à travers mes onglets dans l'ordre dans lequel ils sont affichés.

Pour paramétrer un résolveur DoH, en l’occurrence celui de Stéphane Bortzmeyer : toujours dans les Préférences / onglet Général, tout en bas à la rubrique Paramètres réseau je clique sur le bouton « Paramètres » puis je coche « Activer le DNS via HTTPS » (si ce n'est déjà fait). Je sélectionne ensuite « Utilisez le fournisseur : personnalisé » et spécifie à la ligne du dessous : doh.bortzmeyer.fr

Dans les Préférences, cette fois à l'onglet Recherche, je coche « Ajouter la barre de recherche à la barre d’outils » (je trouve cette configuration plus pratique pour utiliser mes multiples moteurs de recherche – cf ci-après).

On finit le tour des Préférences en allant à l'onglet Vie privée et sécurité pour passer la Protection renforcée contre le pistage en mode « Stricte » (contre le pistage Third-Party) puis pour sélectionner tout en bas de la page « Activer le mode HTTPS uniquement dans toutes les fenêtres ».

Réglages avancés via la page about:config

Ayant un processeur Intel avec cœur graphique intégré et un écran de définition modeste, j'aurais tort de me priver de l’accélération matérielle du rendu des pages via WebRender : je passe simplement la valeur gfx.webrender.all à true et relance le navigateur (à la page about:support, la ligne Composition doit maintenant indiquer WebRender).

Je demande ensuite l'accélération matérielle du décodage vidéo (nécessite que WebRender soit activé – cf ci-dessus) en passant la valeur media.ffmpeg.vaapi.enabled à true.
Puis, comme mon processeur ne prend pas en charge le décodage matériel de VP8 ni VP9 qui sont utilisés dans le format WebM, je passe la clé media.webm.enabled à false pour que les sites qui proposent à la fois du H264 et du WebM me servent le premier (vous pouvez préférer utiliser une extension comme celle-ci ou celle-là mais j'essaye de limiter le nombre d'extensions que j'installe).

Pour économiser mon SSD, je demande de stocker les fichiers temporaires en mémoire vive en passant browser.cache.disk.enable à false.

Pour supprimer tous les popups et les rediriger vers les onglets, je passe browser.link.open_newwindow.restriction à 0.

Pour désactiver le punycode (en savoir plus), je passe network.IDN_show_punycode à true.

Contre le pistage First-Party, j'active la fonction First-Party Isolation en passant privacy.firstparty.isolate à true.

Et pour renforcer la protection générale contre la prise d'empreinte numérique (objectif probablement impossible à atteindre malheureusement), je passe privacy.resistFingerprinting à true.

Les huit extensions que je réinstalle

Par ordre alphabétique :

Moteurs de recherche

En attendant la fin programmée des moteurs de recherche OpenSearch, je me gave des moteurs disponibles sur Mycroft Projet.

Gravatar de antistress
Original post of antistress.Votez pour ce billet sur Planet Libre.