anchor.diff:        await pool.close();
       }
     });
  +
  +  print(JsonEncoder.withIndent('  ').convert(_removedCount));
   }
   
   Future _processWithQuery<T extends ExpandoModel>(Query<T> query) async {
     await for (T m in query.run()) {
  -    if (m.additionalProperties.isNotEmpty) {
  -      await dbService.withTransaction((tx) async {
  -        final entry = (await tx.lookup<T>([m.key])).single;
  -        entry.additionalProperties.clear();
  -        tx.queueMutations(inserts: [entry]);
  -        await tx.commit();
  -      });
  -    }
  +    await _clearAdditionalProperties(m);
     }
   }
   
  -Future _processPackage(
  -  String package, {
  -  bool packageEntityNeedsUpdate = true,
  -}) async {
  -  print('Processing package: $package');
  -  final pkgKey = dbService.emptyKey.append(Package, id: package);
  +Future _processPackage(Package package) async {
  +  print('Processing package: ${package.name}');
   
  -  final versionQuery = dbService.query<PackageVersion>(ancestorKey: pkgKey);
  +  final versionQuery =
  +      dbService.query<PackageVersion>(ancestorKey: package.key);
     await _processWithQuery(versionQuery);
   
     final pubspecQuery = dbService.query<PackageVersionPubspec>()
  -    ..filter('package =', package);
  +    ..filter('package =', package.name);
     await _processWithQuery(pubspecQuery);
   
     final infoQuery = dbService.query<PackageVersionInfo>()
  -    ..filter('package =', package);
  +    ..filter('package =', package.name);
     await _processWithQuery(infoQuery);
   
  -  if (packageEntityNeedsUpdate) {
  -    await dbService.withTransaction((tx) async {
  -      final p = (await dbService.lookup<Package>([pkgKey])).single;
  -      if (p.additionalProperties.isEmpty) {
  -        await tx.rollback();
  -        return;
  -      }
  -      p.additionalProperties.clear();
  -      tx.queueMutations(inserts: [p]);
  -      await tx.commit();
  -    });
  +  await _clearAdditionalProperties(package);
  +}
  +
  +Future _clearAdditionalProperties<T extends ExpandoModel>(T model) async {
  +  if (model.additionalProperties.isEmpty) {
  +    return;
     }
  +  final props = model.additionalProperties.keys.toList();
  +  props.sort();
  +  if (_isVerbose) {
  +    print('Clearing ${_key(model.key)} of ${props.join(', ')}');
  +  }
  +
  +  for (String prop in props) {
  +    final statKey = '${model.key.type.toString()}.$prop';
  +    _removedCount[statKey] = (_removedCount[statKey] ?? 0) + 1;
  +  }
  +
  +  if (_isDryRun) return;
  +  await dbService.withTransaction((tx) async {
  +    final entry = (await tx.lookup<T>([model.key])).single;
  +    entry.additionalProperties.clear();
  +    tx.queueMutations(inserts: [entry]);
  +    await tx.commit();
  +  });
   }
  +
  +String _key(Key key) {
  +  final sb = StringBuffer();
  +  if (key.parent != null && key.parent.id != null) {
  +    sb.write(_key(key.parent));
  +    sb.write('/');
  +  }
  +  sb.write(key.type);
  +  sb.write('(');
  +  sb.write(key.id);
  +  sb.write(')');
  +  return sb.toString();
  +}
  
positive.code: // BSD-style license that can be found in the LICENSE file.
  
  import 'dart:async';
  
  import 'package:args/args.dart';
  import 'package:gcloud/db.dart';
  
negative.code:     return;
    }
  
    _isDryRun = argv['dry-run'] as bool;
    _isVerbose = argv['verbose'] as bool;
    final concurrency = int.parse(argv['concurrency'] as String);
    final package = argv['package'] as String;
    await withProdServices(() async {
      if (package != null) {
        final p = (await dbService.lookup<Package>(
                [dbService.emptyKey.append(Package, id: package)]))
            .single;
        await _processPackage(p);
      } else {
        final pool = Pool(concurrency);
        final futures = <Future>[];
  
        await for (Package p in dbService.query<Package>().run()) {
          final f = pool.withResource(() => _processPackage(p));
          futures.add(f);
        }
  
  