DO NOT MERGE: feat(sharding): add label-driven predicate routing#9574
Draft
mlwelles wants to merge 12 commits intodgraph-io:mainfrom
Draft
DO NOT MERGE: feat(sharding): add label-driven predicate routing#9574mlwelles wants to merge 12 commits intodgraph-io:mainfrom
mlwelles wants to merge 12 commits intodgraph-io:mainfrom
Conversation
Add @Label(name) directive to predicates, allowing them to be pinned to specific alpha groups that share the same label. This creates implicit sub-clusters for data isolation. Key changes: - Add label field to Member, Tablet, and SchemaUpdate protos - Parse @Label(X) directive in schema (schema/parse.go) - Route labeled predicates to matching labeled groups in Zero - Require explicit label parameter in Tablet(), BelongsTo(), ForceTablet() - Block moveTablet for labeled predicates - Prevent unlabeled predicates from being assigned to labeled groups Label routing flow: 1. Alpha with --label=X registers with Zero, Member.Label is set 2. Schema with @Label(X) is parsed, SchemaUpdate.Label is set 3. Zero routes tablet to group where Member.Label matches 4. Rebalancer skips tablets with non-empty Label All tablet lookup functions now require explicit label parameter to ensure correct routing for new predicates during schema mutations.
# Conflicts: # go.mod
- Update trunk go runtime from 1.24.3 to 1.25.6 to match go.mod - Fix trailing newline formatting in label test files
- Use closure with defer for RLock/RUnlock in Inform() to prevent lock leaks on error paths - Add getGroupLabel() helper that handles its own locking, keeping groupLabel() for callers that already hold the lock - Move checkSchemaTablet function closer to its usage in proposeAndWait - Add tablet_label span attribute for better observability
Rename for consistency - drop redundant 'Id' suffix since return types already indicate the value type: - labelGroupId → labelGroup - isLabeledGroupId → isLabeledGroup - firstUnlabeledGroupId → firstUnlabeledGroup
Programs are assigned at mutation time via declared programs in security context
Add IsLabeled() methods to Tablet, Member, and SchemaUpdate protobuf types, replacing raw `Label != ""` checks with nil-safe, semantic helpers. This centralizes the label-check logic and makes the intent clearer at each call site.
Design document for extending label-based predicate routing to support entity-level routing via dgraph.label. Key decisions: - Sub-tablet keys: predicate@label (backward compat for unlabeled) - Entity label stored on group 1 as reserved predicate - Two-phase mutation routing (extract labels, then route) - Query fan-out to all authorized sub-tablets - Synchronous reclassification following predicate-move pattern - Entity label > predicate @Label > unlabeled priority
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
DO NOT MERGE
POC/Experiment for review / discussion purposes only.
This is a proof-of-concept implementation for label-based predicate sharding and program-based data compartmentalization. It is intended for review and testing purposes only.
Summary
Two complementary security features that together enable multi-level data isolation in Dgraph:
@labeldirective) — Routes predicates to specific alpha groups, creating physically isolated sub-clusters.dgraph.programsfacets) — Applies need-to-know access control at the posting level, filtering data based on user program membership.Feature 1: Label-Based Predicate Routing
Concept
Add
@label(name)directive to predicates, allowing them to be pinned to specific alpha groups that share the same label. This creates implicit sub-clusters for data isolation.Changes
Protocol Buffers (
protos/pb.proto,protos/pb/pb.pb.go)labelfield toMember(field 15),Tablet(field 12), andSchemaUpdate(field 16)protos/pb/labeled.gowith nil-safeIsLabeled()helpers forTablet,Member, andSchemaUpdateSchema Parsing (
schema/parse.go,schema/schema.go)@label(X)directive in schema definitionsGetLabel()accessor for retrieving a predicate's label from schema stateAlpha CLI (
dgraph/cmd/alpha/run.go,x/config.go)--labelflag on alpha, stored inWorkerOptions.LabelMember.LabelZero Routing (
dgraph/cmd/zero/zero.go,raft.go,tablet.go)Inform()andShouldServe(): Route labeled predicates to matching labeled groupschooseTablet(): Skip labeled predicates during rebalancing (pinned)movePredicate(): Block moves when destination group label doesn't matchgroupLabel(),labelGroup(),isLabeledGroup(),firstUnlabeledGroup()Worker Routing (
worker/groups.go,mutation.go,proposal.go,predicate_move.go,online_restore.go)Tablet(key, label),BelongsTo(key, label),ForceTablet(key, label)now accept label parameterServesTablet()auto-resolves label from stored schemaGetTabletLabel()returns cached tablet label for authorizationpopulateMutationMap()passes label from schema for correct group routingrunSchemaMutation()records schema metadata for labeled predicates served by other groupsproposeAndWait()addscheckSchemaTablet()that allows labeled predicates to be served by different groupsonline_restore.go) pass labels when force-creating tabletsLabel Routing Flow
Example Usage
Feature 2: Program-Based Data Compartmentalization
Concept
Implements need-to-know compartmentalization using
dgraph.programsfacets. When a user has programs in their auth context, those programs are automatically attached to every mutation (as a facet). During queries, postings are filtered so users only see data matching their program membership.Changes
Auth Context (
x/auth/)New package providing security context extraction and authorization logic:
context.goAuthContextstruct (UserID, Namespace, Groups, Level, Programs, IsNil), context helpersextractor.goExtractOrNil()— extracts auth from context or returns nil-token sentinellevels.goCanAccess(userLevel, requiredLabel)— hierarchical level comparison (top_secret > secret > confidential > unclassified)programs.goHasAnyProgram()— OR-logic program membership checktransport.goUnaryInterceptor/StreamInterceptor— gRPC interceptors that extractx-dgraph-levelandx-dgraph-programsheaders intoAuthContextmock.goMockAuthContext()for testing*_test.goMutation Path (
edgraph/server.go)injectProgramFacets()— Auto-attachesdgraph.programsfacet to every SET edge based on auth contextcheckMutationProgramAuth()— Pre-Raft authorization: checks if user can modify existing program-protected dataprogramFacetCache— Write-through cache for recently committed program facets (addresses MemoryLayer read-through limitation)updateProgramFacetCache()— Updates cache after successfulCommitOverNetworkQuery Path (
edgraph/access.go)authorizeLabeledPreds()— Filters predicates by security level (hierarchical), runs independently of ACLauthorizeQuery()— Extended to merge label-blocked and ACL-blocked predicates; both checks must passQuery Execution (
worker/task.go)checkProgramAuthorization()— Core posting-level filter: checksdgraph.programsfacet against user's programs (OR logic)handleValuePostings,handleUidPostings,handleHasFunction,handleHasWithOrderFunction,handleRegexFunction,handleCompareFunction,handleMatchFunctionfacetsFilterUidPostingList()andfacetsFilterValuePostingList()— Extended with program checksfuncArgsstruct gainsctxfield for threading auth context through query executionPosting Store (
posting/lists.go)GetNoStoreSafe()— Nil-safe variant ofGetNoStorefor use before posting package initializationKeys & Config (
x/keys.go)ProgramFacetKey = "dgraph.programs"— Reserved facet key constantIsReservedFacetKey()for validationProgram Authorization Flow
Testing
Test Cluster Configuration
Docker cluster in
systest/label/docker-compose.yml:zero1alpha1alpha2secret@label(secret)predicatesalpha3top_secret@label(top_secret)predicatesLabel Routing Tests (
systest/label/label_test.go)TestLabeledAlphaRegistration--label=Xregister label with ZeroTestLabeledPredicateRouting@label(X)predicates route to matching groupTestLabeledPredicateDataIsolationTestLabeledPredicateCannotBeMoved/moveTabletvalidates label compatibilityTestUnlabeledPredicateNotOnLabeledGroupTestMissingLabelGroupErrorProgram Authorization Tests (
systest/label/label_auth_test.go)TestLabelQueryAuthorizationTestProgramInsertFacetInjectionTestProgramQueryFilteringTestProgramMutationAuthorizationTestProgramQueryFilteringOnUnlabeledPredicates@labelRun Tests
Files Changed (from main)
protos/pb.proto,protos/pb/pb.pb.go,protos/pb/labeled.goschema/parse.go,schema/schema.godgraph/cmd/alpha/run.go,x/config.godgraph/cmd/zero/zero.go,raft.go,tablet.gox/auth/*.go(7 files)edgraph/access.go,edgraph/server.goworker/groups.go,mutation.go,proposal.go,predicate_move.go,online_restore.goworker/task.goposting/lists.gox/keys.gosystest/label/(3 files)testutil/zero.go.gitignore