Aller au contenu

Conventions Protobuf -- EIP-MemTide

Portee

Ce document definit les conventions applicables a tous les fichiers Protocol Buffers (.proto) du projet MemTide. Il concerne le depot memtide-proto, source unique de verite pour les definitions gRPC partagees entre les composants de l'organisation (memtide, memtide-gateway, memtide-yapper, FrontL). Tout contributeur modifiant ou creant des fichiers .proto doit s'y conformer sans exception.


1. Syntaxe

Tous les fichiers utilisent exclusivement proto3. L'utilisation de proto2 est interdite.

syntax = "proto3";
package memtide.proto;
option go_package = "github.com/EIP-memtide/memtide-proto/gen/go/memtidepb";

2. Conventions de nommage

Element Convention Exemple
Messages PascalCase TopologyData, MemoryBlock
Champs snake_case total_bytes, node_id
Enums PascalCase TierType, NumaNodeType
Valeurs d'enum UPPER_CASE NUMA_DRAM, CXL_EXPANDER
Services PascalCase TopologyService, HealthService
Methodes RPC PascalCase GetTopology, MigratePages
Fichiers snake_case.proto topology_data.proto
Packages lowercase.dotted memtide.proto, memtide.v1

Regles supplementaires :

  • Les noms de messages et services sont des noms : TopologyData, MigrationStats.
  • Les methodes RPC sont des verbes : GetTopology, StreamPages, MigrateMemory.
  • Les champs booleens utilisent un prefixe semantique : is_cxl, has_data, ok.
  • Les champs de quantite incluent l'unite : total_bytes, uptime_sec, base_freq_ghz.

3. Enumerations

Valeur zero obligatoire

La premiere valeur doit etre zero avec le suffixe _UNSPECIFIED. Elle sert de sentinelle et ne represente jamais une valeur metier.

enum TierType {
  TIER_TYPE_UNSPECIFIED = 0;
  TIER_TYPE_NUMA_DRAM   = 1;
  TIER_TYPE_CXL         = 2;
}

Prefixe court

Les valeurs d'enum sont prefixees par le nom de l'enum en UPPER_CASE pour eviter les collisions :

Enum Prefixe Exemple de valeurs
TierType TIER_TYPE_ TIER_TYPE_UNSPECIFIED, TIER_TYPE_NUMA_DRAM
NumaNodeType NUMA_NODE_TYPE_ NUMA_NODE_TYPE_UNSPECIFIED, NUMA_NODE_TYPE_DRAM

Declarer l'enum au niveau du fichier si elle est referencee par plusieurs messages ; l'imbriquer dans un message uniquement si elle lui est strictement privee.


4. Compatibilite wire

La compatibilite wire est sacree. Toute rupture empeche les anciens clients de communiquer avec les nouveaux serveurs.

Action interdite Consequence
Changer le tag numerique d'un champ Corruption silencieuse
Changer le type d'un champ Deserialisation incorrecte
Supprimer un champ Donnees perdues silencieusement
Reutiliser un tag deja utilise Corruption silencieuse
Action autorisee Securite
Ajouter un nouveau champ Toujours safe
Ajouter une nouvelle valeur d'enum Safe (zero reste UNSPECIFIED)
Ajouter une nouvelle methode RPC Safe
Renommer un champ Safe (wire = tag, pas nom)

Champs retires : reserved

Ne jamais supprimer un champ. Le marquer reserved pour interdire la reutilisation du tag et du nom :

message TopologyData {
  reserved 6;
  reserved "legacy_field";

  CpuInfo           cpu_info             = 1;
  repeated NumaNode nodes                = 2;
  repeated int32    distance_matrix_flat = 4;
}

Verifier avant chaque commit : buf breaking --against '.git#branch=dev'


5. Definitions de services

Nommage des RPC

Pattern Prefixe Exemple
Lecture unitaire Get GetTopology, GetMigrationStats
Liste List ListProcesses, ListNodes
Action / mutation Verbe d'action MigrateMemory, SetPid
Stream serveur Stream StreamPages, StreamTopology
Health check Check Check

Streaming

  • Un RPC de streaming serveur retourne stream <MessageType>.
  • Utiliser google.protobuf.Empty pour les streams sans parametre.
  • Preferer un message de requete dedie si des filtres sont necessaires.
rpc StreamPages (google.protobuf.Empty) returns (stream PageSnapshot);
rpc StreamNodeMetrics (StreamNodeMetricsRequest) returns (stream NodeMetrics);

Messages Request / Response

Chaque RPC definit une paire <MethodName>Request / <MethodName>Response (sauf usage de google.protobuf.Empty). Les reponses avec statut d'erreur incluent string error_message.

message MigrateMemoryRequest {
  uint32 pid       = 1;
  uint64 page_addr = 2;
}

message MigrateMemoryResponse {
  bool   ok            = 1;
  string error_message = 2;
}

6. Organisation des fichiers

Structure du depot

memtide-proto/
  memtide.proto        # Source unique de verite
  buf.yaml             # Lint (STANDARD) + breaking (WIRE)
  buf.gen.yaml         # Plugins de generation (Go, C++)
  gen/                 # Code genere -- NE JAMAIS EDITER
    go/                # Stubs Go (memtide-gateway)
    cpp/               # Stubs C++ (memtide, FrontL)
  flake.nix            # Environnement Nix reproductible

Evolution vers plusieurs fichiers

Si le schema grandit, decouper selon la regle un service par fichier :

Fichier Contenu
topology.proto TopologyService et ses messages
migration.proto MigrateMemoryService, MigrationStatsService
health.proto HealthService, HealthResponse
common.proto Messages partages (CpuInfo, NumaNode, etc.)

Tous les fichiers partagent le meme package memtide.proto et s'importent via import "common.proto";.


7. Code genere et workflow

Les fichiers dans gen/ sont generes par protoc via Buf. Il est strictement interdit de les modifier manuellement.

Plugin Sortie Consommateurs
protoc-gen-cpp gen/cpp/ memtide, FrontL
protoc-gen-grpc-cpp gen/cpp/ memtide, FrontL
protoc-gen-go gen/go/ memtide-gateway
protoc-gen-go-grpc gen/go/ memtide-gateway

Regeneration (depuis un shell Nix) : nix run .#gen-cpp et nix run .#gen-go. Commiter les fichiers generes avec les .proto modifies dans le meme PR.


8. Linting et validation

Le projet utilise Buf CLI (buf.yaml : lint STANDARD, breaking WIRE).

Commande Role
buf lint Respect des regles STANDARD
buf breaking --against '.git#branch=dev' Compatibilite wire

La categorie STANDARD impose notamment : PascalCase pour messages/enums/services, snake_case pour les champs, valeur zero _UNSPECIFIED dans les enums, prefixe sur les valeurs d'enum, package et go_package declares. Toute violation bloque le commit.


9. Formatage et commentaires

Indentation

  • 2 espaces (pas de tabulations).
  • Une ligne vide entre chaque definition de message, enum ou service.

Commentaires

  • Utiliser exclusivement // (pas /* */).
  • Placer les commentaires au-dessus de l'element documente.
  • Documenter chaque message et service ; documenter les champs non triviaux.
  • Les commentaires sont rediges en anglais americain (U.S. English).
// Hardware topology of the host machine.
// Includes CPU info, NUMA nodes, sockets, and distance matrix.
message TopologyData {
  // Processor identification and capabilities.
  CpuInfo cpu_info = 1;

  // All NUMA nodes discovered on the host.
  repeated NumaNode nodes = 2;

  // Row-major flattened NUMA distance matrix.
  repeated int32 distance_matrix_flat = 4;

  // Side length of the square distance matrix.
  uint32 distance_matrix_size = 5;
}

Checklist avant commit

  1. syntax = "proto3", package et go_package declares.
  2. Nommage conforme (section 2) ; enums avec zero _UNSPECIFIED et prefixe (section 3).
  3. Aucun tag existant modifie, change de type ou reutilise (section 4).
  4. buf lint et buf breaking --against '.git#branch=dev' passent sans erreur.
  5. Stubs generes a jour (nix run .#gen-cpp, nix run .#gen-go) et commites avec les .proto.
  6. Commentaires en anglais, places au-dessus des elements documentes.