[DEPRECATED] Go implementation of plcbundle
at rust-test 193 lines 4.9 kB view raw
1package didindex 2 3import ( 4 "context" 5 "fmt" 6 "sort" 7 "sync" 8 9 "tangled.org/atscan.net/plcbundle/internal/plcclient" 10) 11 12// GetDIDOperations retrieves all operations for a DID WITH location metadata 13// Returns operations with bundle/position info (includes nullified operations) 14func (dim *Manager) GetDIDOperations(ctx context.Context, did string, provider BundleProvider) ([]OpLocationWithOperation, error) { 15 if err := plcclient.ValidateDIDFormat(did); err != nil { 16 return nil, err 17 } 18 19 if !dim.Exists() { 20 return nil, fmt.Errorf("DID index not available - run 'plcbundle index build' to enable DID lookups") 21 } 22 23 locations, err := dim.GetDIDLocations(did) 24 if err != nil { 25 return nil, err 26 } 27 28 if len(locations) == 0 { 29 return []OpLocationWithOperation{}, nil 30 } 31 32 // Group by bundle 33 bundleMap := make(map[uint16][]OpLocation) 34 for _, loc := range locations { 35 bundleMap[loc.Bundle()] = append(bundleMap[loc.Bundle()], loc) 36 } 37 38 var results []OpLocationWithOperation 39 for bundleNum, locs := range bundleMap { 40 positions := make([]int, len(locs)) 41 for i, l := range locs { 42 positions[i] = l.PositionInt() 43 } 44 opsMap, err := provider.LoadOperations(ctx, int(bundleNum), positions) 45 if err != nil { 46 dim.logger.Printf("Warning: failed to load bundle %d: %v", bundleNum, err) 47 continue 48 } 49 for i, l := range locs { 50 if op, ok := opsMap[positions[i]]; ok { 51 results = append(results, OpLocationWithOperation{ 52 Operation: *op, 53 Bundle: l.BundleInt(), 54 Position: l.PositionInt(), 55 }) 56 } 57 } 58 } 59 60 // Sort by time 61 sort.Slice(results, func(i, j int) bool { 62 return results[i].Operation.CreatedAt.Before(results[j].Operation.CreatedAt) 63 }) 64 65 return results, nil 66} 67 68// GetLatestDIDOperation returns the most recent non-nullified operation 69func (dim *Manager) GetLatestDIDOperation(ctx context.Context, did string, provider BundleProvider) (*plcclient.PLCOperation, error) { 70 if err := plcclient.ValidateDIDFormat(did); err != nil { 71 return nil, err 72 } 73 74 if !dim.Exists() { 75 return nil, fmt.Errorf("DID index not available - run 'plcbundle index build' to enable DID lookups") 76 } 77 78 locations, err := dim.GetDIDLocations(did) 79 if err != nil { 80 return nil, err 81 } 82 83 if len(locations) == 0 { 84 return nil, fmt.Errorf("DID not found") 85 } 86 87 // Find latest non-nullified location 88 var latestLoc *OpLocation 89 for i := range locations { 90 if locations[i].Nullified() { 91 continue 92 } 93 94 if latestLoc == nil { 95 latestLoc = &locations[i] 96 } else { 97 if locations[i].Bundle() > latestLoc.Bundle() || 98 (locations[i].Bundle() == latestLoc.Bundle() && locations[i].Position() > latestLoc.Position()) { 99 latestLoc = &locations[i] 100 } 101 } 102 } 103 104 if latestLoc == nil { 105 return nil, fmt.Errorf("no valid operations found (all nullified)") 106 } 107 108 // Load ONLY the specific operation (efficient!) 109 return provider.LoadOperation(ctx, latestLoc.BundleInt(), latestLoc.PositionInt()) 110} 111 112// BatchGetDIDLocations retrieves locations for multiple DIDs efficiently 113// Returns map[did][]OpLocation - only locations, no operation loading 114func (dim *Manager) BatchGetDIDLocations(dids []string) (map[string][]OpLocation, error) { 115 if !dim.Exists() { 116 return nil, fmt.Errorf("DID index not available") 117 } 118 119 // Group DIDs by shard to minimize shard loads 120 type shardQuery struct { 121 shardNum uint8 122 identifiers []string 123 didMap map[string]string // identifier -> original DID 124 } 125 126 shardQueries := make(map[uint8]*shardQuery) 127 128 for _, did := range dids { 129 identifier, err := extractDIDIdentifier(did) 130 if err != nil { 131 continue 132 } 133 134 shardNum := dim.calculateShard(identifier) 135 136 if shardQueries[shardNum] == nil { 137 shardQueries[shardNum] = &shardQuery{ 138 shardNum: shardNum, 139 identifiers: make([]string, 0), 140 didMap: make(map[string]string), 141 } 142 } 143 144 sq := shardQueries[shardNum] 145 sq.identifiers = append(sq.identifiers, identifier) 146 sq.didMap[identifier] = did 147 } 148 149 if dim.verbose { 150 dim.logger.Printf("DEBUG: Batch lookup: %d DIDs across %d shards", len(dids), len(shardQueries)) 151 } 152 153 // Process each shard (load once, search multiple times) 154 results := make(map[string][]OpLocation) 155 var mu sync.Mutex 156 157 var wg sync.WaitGroup 158 for _, sq := range shardQueries { 159 wg.Add(1) 160 go func(query *shardQuery) { 161 defer wg.Done() 162 163 // Load shard once 164 shard, err := dim.loadShard(query.shardNum) 165 if err != nil { 166 if dim.verbose { 167 dim.logger.Printf("DEBUG: Failed to load shard %02x: %v", query.shardNum, err) 168 } 169 return 170 } 171 defer dim.releaseShard(shard) 172 173 if shard.data == nil { 174 return 175 } 176 177 // Search for all identifiers in this shard 178 for _, identifier := range query.identifiers { 179 locations := dim.searchShard(shard, identifier) 180 if len(locations) > 0 { 181 originalDID := query.didMap[identifier] 182 mu.Lock() 183 results[originalDID] = locations 184 mu.Unlock() 185 } 186 } 187 }(sq) 188 } 189 190 wg.Wait() 191 192 return results, nil 193}