@malloy-publisher/server 0.0.83 → 0.0.84

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js CHANGED
@@ -235636,8 +235636,8 @@ class DatabaseController {
235636
235636
  this.projectStore = projectStore;
235637
235637
  }
235638
235638
  async listDatabases(projectName, packageName) {
235639
- const project = await this.projectStore.getProject(projectName);
235640
- const p = await project.getPackage(packageName);
235639
+ const project = await this.projectStore.getProject(projectName, false);
235640
+ const p = await project.getPackage(packageName, false);
235641
235641
  return p.listDatabases();
235642
235642
  }
235643
235643
  }
@@ -243482,6 +243482,214 @@ function initializeMcpServer(projectStore) {
243482
243482
  var fs7 = __toESM(require("fs/promises"));
243483
243483
  var path6 = __toESM(require("path"));
243484
243484
 
243485
+ // ../../node_modules/async-mutex/index.mjs
243486
+ var E_TIMEOUT = new Error("timeout while waiting for mutex to become available");
243487
+ var E_ALREADY_LOCKED = new Error("mutex already locked");
243488
+ var E_CANCELED = new Error("request for lock canceled");
243489
+ var __awaiter$2 = function(thisArg, _arguments, P, generator) {
243490
+ function adopt(value) {
243491
+ return value instanceof P ? value : new P(function(resolve) {
243492
+ resolve(value);
243493
+ });
243494
+ }
243495
+ return new (P || (P = Promise))(function(resolve, reject) {
243496
+ function fulfilled(value) {
243497
+ try {
243498
+ step(generator.next(value));
243499
+ } catch (e) {
243500
+ reject(e);
243501
+ }
243502
+ }
243503
+ function rejected(value) {
243504
+ try {
243505
+ step(generator["throw"](value));
243506
+ } catch (e) {
243507
+ reject(e);
243508
+ }
243509
+ }
243510
+ function step(result) {
243511
+ result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
243512
+ }
243513
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
243514
+ });
243515
+ };
243516
+
243517
+ class Semaphore {
243518
+ constructor(_value, _cancelError = E_CANCELED) {
243519
+ this._value = _value;
243520
+ this._cancelError = _cancelError;
243521
+ this._queue = [];
243522
+ this._weightedWaiters = [];
243523
+ }
243524
+ acquire(weight = 1, priority = 0) {
243525
+ if (weight <= 0)
243526
+ throw new Error(`invalid weight ${weight}: must be positive`);
243527
+ return new Promise((resolve, reject) => {
243528
+ const task = { resolve, reject, weight, priority };
243529
+ const i = findIndexFromEnd(this._queue, (other) => priority <= other.priority);
243530
+ if (i === -1 && weight <= this._value) {
243531
+ this._dispatchItem(task);
243532
+ } else {
243533
+ this._queue.splice(i + 1, 0, task);
243534
+ }
243535
+ });
243536
+ }
243537
+ runExclusive(callback_1) {
243538
+ return __awaiter$2(this, arguments, undefined, function* (callback, weight = 1, priority = 0) {
243539
+ const [value, release] = yield this.acquire(weight, priority);
243540
+ try {
243541
+ return yield callback(value);
243542
+ } finally {
243543
+ release();
243544
+ }
243545
+ });
243546
+ }
243547
+ waitForUnlock(weight = 1, priority = 0) {
243548
+ if (weight <= 0)
243549
+ throw new Error(`invalid weight ${weight}: must be positive`);
243550
+ if (this._couldLockImmediately(weight, priority)) {
243551
+ return Promise.resolve();
243552
+ } else {
243553
+ return new Promise((resolve) => {
243554
+ if (!this._weightedWaiters[weight - 1])
243555
+ this._weightedWaiters[weight - 1] = [];
243556
+ insertSorted(this._weightedWaiters[weight - 1], { resolve, priority });
243557
+ });
243558
+ }
243559
+ }
243560
+ isLocked() {
243561
+ return this._value <= 0;
243562
+ }
243563
+ getValue() {
243564
+ return this._value;
243565
+ }
243566
+ setValue(value) {
243567
+ this._value = value;
243568
+ this._dispatchQueue();
243569
+ }
243570
+ release(weight = 1) {
243571
+ if (weight <= 0)
243572
+ throw new Error(`invalid weight ${weight}: must be positive`);
243573
+ this._value += weight;
243574
+ this._dispatchQueue();
243575
+ }
243576
+ cancel() {
243577
+ this._queue.forEach((entry) => entry.reject(this._cancelError));
243578
+ this._queue = [];
243579
+ }
243580
+ _dispatchQueue() {
243581
+ this._drainUnlockWaiters();
243582
+ while (this._queue.length > 0 && this._queue[0].weight <= this._value) {
243583
+ this._dispatchItem(this._queue.shift());
243584
+ this._drainUnlockWaiters();
243585
+ }
243586
+ }
243587
+ _dispatchItem(item) {
243588
+ const previousValue = this._value;
243589
+ this._value -= item.weight;
243590
+ item.resolve([previousValue, this._newReleaser(item.weight)]);
243591
+ }
243592
+ _newReleaser(weight) {
243593
+ let called = false;
243594
+ return () => {
243595
+ if (called)
243596
+ return;
243597
+ called = true;
243598
+ this.release(weight);
243599
+ };
243600
+ }
243601
+ _drainUnlockWaiters() {
243602
+ if (this._queue.length === 0) {
243603
+ for (let weight = this._value;weight > 0; weight--) {
243604
+ const waiters = this._weightedWaiters[weight - 1];
243605
+ if (!waiters)
243606
+ continue;
243607
+ waiters.forEach((waiter) => waiter.resolve());
243608
+ this._weightedWaiters[weight - 1] = [];
243609
+ }
243610
+ } else {
243611
+ const queuedPriority = this._queue[0].priority;
243612
+ for (let weight = this._value;weight > 0; weight--) {
243613
+ const waiters = this._weightedWaiters[weight - 1];
243614
+ if (!waiters)
243615
+ continue;
243616
+ const i = waiters.findIndex((waiter) => waiter.priority <= queuedPriority);
243617
+ (i === -1 ? waiters : waiters.splice(0, i)).forEach((waiter) => waiter.resolve());
243618
+ }
243619
+ }
243620
+ }
243621
+ _couldLockImmediately(weight, priority) {
243622
+ return (this._queue.length === 0 || this._queue[0].priority < priority) && weight <= this._value;
243623
+ }
243624
+ }
243625
+ function insertSorted(a, v) {
243626
+ const i = findIndexFromEnd(a, (other) => v.priority <= other.priority);
243627
+ a.splice(i + 1, 0, v);
243628
+ }
243629
+ function findIndexFromEnd(a, predicate) {
243630
+ for (let i = a.length - 1;i >= 0; i--) {
243631
+ if (predicate(a[i])) {
243632
+ return i;
243633
+ }
243634
+ }
243635
+ return -1;
243636
+ }
243637
+ var __awaiter$1 = function(thisArg, _arguments, P, generator) {
243638
+ function adopt(value) {
243639
+ return value instanceof P ? value : new P(function(resolve) {
243640
+ resolve(value);
243641
+ });
243642
+ }
243643
+ return new (P || (P = Promise))(function(resolve, reject) {
243644
+ function fulfilled(value) {
243645
+ try {
243646
+ step(generator.next(value));
243647
+ } catch (e) {
243648
+ reject(e);
243649
+ }
243650
+ }
243651
+ function rejected(value) {
243652
+ try {
243653
+ step(generator["throw"](value));
243654
+ } catch (e) {
243655
+ reject(e);
243656
+ }
243657
+ }
243658
+ function step(result) {
243659
+ result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
243660
+ }
243661
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
243662
+ });
243663
+ };
243664
+
243665
+ class Mutex {
243666
+ constructor(cancelError) {
243667
+ this._semaphore = new Semaphore(1, cancelError);
243668
+ }
243669
+ acquire() {
243670
+ return __awaiter$1(this, arguments, undefined, function* (priority = 0) {
243671
+ const [, releaser] = yield this._semaphore.acquire(1, priority);
243672
+ return releaser;
243673
+ });
243674
+ }
243675
+ runExclusive(callback, priority = 0) {
243676
+ return this._semaphore.runExclusive(() => callback(), 1, priority);
243677
+ }
243678
+ isLocked() {
243679
+ return this._semaphore.isLocked();
243680
+ }
243681
+ waitForUnlock(priority = 0) {
243682
+ return this._semaphore.waitForUnlock(1, priority);
243683
+ }
243684
+ release() {
243685
+ if (this._semaphore.isLocked())
243686
+ this._semaphore.release();
243687
+ }
243688
+ cancel() {
243689
+ return this._semaphore.cancel();
243690
+ }
243691
+ }
243692
+
243485
243693
  // src/service/project.ts
243486
243694
  var fs6 = __toESM(require("fs/promises"));
243487
243695
  var path5 = __toESM(require("path"));
@@ -243559,7 +243767,7 @@ async function createConnections(basePath) {
243559
243767
  const config = {
243560
243768
  host: connection.mysqlConnection.host,
243561
243769
  port: connection.mysqlConnection.port,
243562
- username: connection.mysqlConnection.user,
243770
+ user: connection.mysqlConnection.user,
243563
243771
  password: connection.mysqlConnection.password,
243564
243772
  database: connection.mysqlConnection.database
243565
243773
  };
@@ -244198,6 +244406,8 @@ class Scheduler {
244198
244406
  }
244199
244407
 
244200
244408
  // src/service/package.ts
244409
+ var ENABLE_LIST_MODEL_COMPILATION = true;
244410
+
244201
244411
  class Package {
244202
244412
  projectName;
244203
244413
  packageName;
@@ -244223,18 +244433,57 @@ class Package {
244223
244433
  static async create(projectName, packageName, packagePath, projectConnections) {
244224
244434
  const startTime = performance.now();
244225
244435
  await Package.validatePackageManifestExistsOrThrowError(packagePath);
244436
+ const manifestValidationTime = performance.now();
244437
+ logger.info("Package manifest validation completed", {
244438
+ packageName,
244439
+ duration: manifestValidationTime - startTime,
244440
+ unit: "ms"
244441
+ });
244226
244442
  try {
244227
244443
  const packageConfig = await Package.readPackageConfig(packagePath);
244444
+ const packageConfigTime = performance.now();
244445
+ logger.info("Package config read completed", {
244446
+ packageName,
244447
+ duration: packageConfigTime - manifestValidationTime,
244448
+ unit: "ms"
244449
+ });
244228
244450
  packageConfig.resource = `${API_PREFIX}/projects/${projectName}/packages/${packageName}`;
244229
244451
  const databases = await Package.readDatabases(packagePath);
244452
+ const databasesTime = performance.now();
244453
+ logger.info("Databases read completed", {
244454
+ packageName,
244455
+ databaseCount: databases.length,
244456
+ duration: databasesTime - packageConfigTime,
244457
+ unit: "ms"
244458
+ });
244230
244459
  const connections = new Map(projectConnections);
244231
244460
  const { malloyConnections: packageConnections } = await createConnections(packagePath);
244461
+ const connectionsTime = performance.now();
244462
+ logger.info("Package connections created", {
244463
+ packageName,
244464
+ connectionCount: packageConnections.size,
244465
+ duration: connectionsTime - databasesTime,
244466
+ unit: "ms"
244467
+ });
244232
244468
  packageConnections.forEach((connection) => {
244233
244469
  connections.set(connection.name, connection);
244234
244470
  });
244235
244471
  connections.set("duckdb", new import_db_duckdb.DuckDBConnection("duckdb", ":memory:", packagePath));
244236
244472
  const models = await Package.loadModels(packageName, packagePath, connections);
244473
+ const modelsTime = performance.now();
244474
+ logger.info("Models loaded", {
244475
+ packageName,
244476
+ modelCount: models.size,
244477
+ duration: modelsTime - connectionsTime,
244478
+ unit: "ms"
244479
+ });
244237
244480
  const scheduler = Scheduler.create(models);
244481
+ const schedulerTime = performance.now();
244482
+ logger.info("Scheduler created", {
244483
+ packageName,
244484
+ duration: schedulerTime - modelsTime,
244485
+ unit: "ms"
244486
+ });
244238
244487
  const endTime = performance.now();
244239
244488
  const executionTime = endTime - startTime;
244240
244489
  this.packageLoadHistogram.record(executionTime, {
@@ -244243,14 +244492,16 @@ class Package {
244243
244492
  });
244244
244493
  return new Package(projectName, packageName, packagePath, packageConfig, databases, models, scheduler);
244245
244494
  } catch (error) {
244246
- logger.error("Error loading package", { error });
244495
+ logger.error(`Error loading package ${packageName}`, { error });
244247
244496
  const endTime = performance.now();
244248
244497
  const executionTime = endTime - startTime;
244249
244498
  this.packageLoadHistogram.record(executionTime, {
244250
244499
  malloy_package_name: packageName,
244251
244500
  status: "error"
244252
244501
  });
244253
- throw new Error("Error loading package: " + error);
244502
+ throw new Error(`Error loading package ${packageName}`, {
244503
+ cause: error
244504
+ });
244254
244505
  }
244255
244506
  }
244256
244507
  getPackageName() {
@@ -244280,10 +244531,12 @@ class Package {
244280
244531
  return modelPath.endsWith(MODEL_FILE_SUFFIX);
244281
244532
  }).map(async (modelPath) => {
244282
244533
  let error;
244283
- try {
244284
- await this.models.get(modelPath)?.getModel();
244285
- } catch (modelError) {
244286
- error = modelError instanceof Error ? modelError.message : undefined;
244534
+ if (ENABLE_LIST_MODEL_COMPILATION) {
244535
+ try {
244536
+ await this.models.get(modelPath)?.getModel();
244537
+ } catch (modelError) {
244538
+ error = modelError instanceof Error ? modelError.message : undefined;
244539
+ }
244287
244540
  }
244288
244541
  return {
244289
244542
  projectName: this.projectName,
@@ -244298,7 +244551,10 @@ class Package {
244298
244551
  return await Promise.all(Array.from(this.models.keys()).filter((modelPath) => {
244299
244552
  return modelPath.endsWith(NOTEBOOK_FILE_SUFFIX);
244300
244553
  }).map(async (modelPath) => {
244301
- const error = this.models.get(modelPath)?.getNotebookError();
244554
+ let error;
244555
+ if (ENABLE_LIST_MODEL_COMPILATION) {
244556
+ error = this.models.get(modelPath)?.getNotebookError();
244557
+ }
244302
244558
  return {
244303
244559
  projectName: this.projectName,
244304
244560
  packageName: this.packageName,
@@ -244380,6 +244636,7 @@ class Package {
244380
244636
  // src/service/project.ts
244381
244637
  class Project {
244382
244638
  packages = new Map;
244639
+ packageMutexes = new Map;
244383
244640
  malloyConnections;
244384
244641
  apiConnections;
244385
244642
  internalConnections;
@@ -244448,10 +244705,9 @@ class Project {
244448
244705
  });
244449
244706
  const packageMetadata = await Promise.all(files.filter((file) => file.isDirectory()).map(async (directory) => {
244450
244707
  try {
244451
- const _package = await this.getPackage(directory.name, false);
244452
- const metadata = _package.getPackageMetadata();
244453
- return metadata;
244454
- } catch {
244708
+ return (await this.getPackage(directory.name, false)).getPackageMetadata();
244709
+ } catch (error) {
244710
+ console.log(`Failed to load package: ${directory.name} due to : ${error}`);
244455
244711
  return;
244456
244712
  }
244457
244713
  }));
@@ -244462,17 +244718,25 @@ class Project {
244462
244718
  }
244463
244719
  }
244464
244720
  async getPackage(packageName, reload) {
244465
- let _package = this.packages.get(packageName);
244466
- if (_package === undefined || reload) {
244721
+ let packageMutex = this.packageMutexes.get(packageName);
244722
+ if (!packageMutex) {
244723
+ packageMutex = new Mutex;
244724
+ this.packageMutexes.set(packageName, packageMutex);
244725
+ }
244726
+ return await packageMutex.runExclusive(async () => {
244727
+ const _package = this.packages.get(packageName);
244728
+ if (_package !== undefined && !reload) {
244729
+ return _package;
244730
+ }
244467
244731
  try {
244468
- _package = await Package.create(this.projectName, packageName, path5.join(this.projectPath, packageName), this.malloyConnections);
244469
- this.packages.set(packageName, _package);
244732
+ const _package2 = await Package.create(this.projectName, packageName, path5.join(this.projectPath, packageName), this.malloyConnections);
244733
+ this.packages.set(packageName, _package2);
244734
+ return _package2;
244470
244735
  } catch (error) {
244471
244736
  this.packages.delete(packageName);
244472
244737
  throw error;
244473
244738
  }
244474
- }
244475
- return _package;
244739
+ });
244476
244740
  }
244477
244741
  }
244478
244742
 
@@ -244595,6 +244859,7 @@ var databaseController = new DatabaseController(projectStore);
244595
244859
  var queryController = new QueryController(projectStore);
244596
244860
  var scheduleController = new ScheduleController(projectStore);
244597
244861
  var mcpApp = import_express.default();
244862
+ initProjects();
244598
244863
  mcpApp.use(MCP_ENDPOINT, import_express.default.json());
244599
244864
  mcpApp.use(MCP_ENDPOINT, import_cors.default());
244600
244865
  mcpApp.all(MCP_ENDPOINT, async (req, res) => {
@@ -244914,3 +245179,12 @@ mainServer.listen(PUBLISHER_PORT, PUBLISHER_HOST, () => {
244914
245179
  var mcpHttpServer = mcpApp.listen(MCP_PORT, PUBLISHER_HOST, () => {
244915
245180
  logger.info(`MCP server listening at http://${PUBLISHER_HOST}:${MCP_PORT}`);
244916
245181
  });
245182
+ function initProjects() {
245183
+ projectStore.listProjects().then((projects) => {
245184
+ projects.forEach((project) => {
245185
+ projectStore.getProject(project.name, false).then((project2) => {
245186
+ project2.listPackages();
245187
+ });
245188
+ });
245189
+ });
245190
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@malloy-publisher/server",
3
3
  "description": "Malloy Publisher Server",
4
- "version": "0.0.83",
4
+ "version": "0.0.84",
5
5
  "main": "dist/server.js",
6
6
  "bin": {
7
7
  "malloy-publisher": "dist/server.js"
@@ -21,21 +21,22 @@
21
21
  "generate-api-types": "bunx openapi-typescript ../../api-doc.yaml --output src/api.ts"
22
22
  },
23
23
  "dependencies": {
24
- "@malloydata/db-bigquery": "^0.0.293",
25
- "@malloydata/db-duckdb": "^0.0.293",
26
- "@malloydata/db-mysql": "^0.0.293",
27
- "@malloydata/db-postgres": "^0.0.293",
28
- "@malloydata/db-snowflake": "^0.0.293",
29
- "@malloydata/db-trino": "^0.0.293",
30
- "@malloydata/malloy": "^0.0.293",
31
- "@malloydata/malloy-sql": "^0.0.293",
32
- "@malloydata/render": "^0.0.293",
24
+ "@malloydata/db-bigquery": "^0.0.295",
25
+ "@malloydata/db-duckdb": "^0.0.295",
26
+ "@malloydata/db-mysql": "^0.0.295",
27
+ "@malloydata/db-postgres": "^0.0.295",
28
+ "@malloydata/db-snowflake": "^0.0.295",
29
+ "@malloydata/db-trino": "^0.0.295",
30
+ "@malloydata/malloy": "^0.0.295",
31
+ "@malloydata/malloy-sql": "^0.0.295",
32
+ "@malloydata/render": "^0.0.295",
33
33
  "@modelcontextprotocol/sdk": "^1.13.2",
34
34
  "@opentelemetry/api": "^1.9.0",
35
35
  "@opentelemetry/auto-instrumentations-node": "^0.57.0",
36
36
  "@opentelemetry/sdk-metrics": "^2.0.0",
37
37
  "@opentelemetry/sdk-node": "^0.200.0",
38
38
  "@opentelemetry/sdk-trace-node": "^2.0.0",
39
+ "async-mutex": "^0.5.0",
39
40
  "body-parser": "^1.20.2",
40
41
  "class-transformer": "^0.5.1",
41
42
  "class-validator": "^0.14.1",
@@ -14,8 +14,8 @@ export class DatabaseController {
14
14
  projectName: string,
15
15
  packageName: string,
16
16
  ): Promise<ApiDatabase[]> {
17
- const project = await this.projectStore.getProject(projectName);
18
- const p = await project.getPackage(packageName);
17
+ const project = await this.projectStore.getProject(projectName, false);
18
+ const p = await project.getPackage(packageName, false);
19
19
  return p.listDatabases();
20
20
  }
21
21
  }
package/src/logger.ts CHANGED
@@ -27,20 +27,19 @@ export const logAxiosError = (error: AxiosError) => {
27
27
  if (error.response) {
28
28
  // The request was made and the server responded with a status code
29
29
  // that falls out of the range of 2xx
30
- logger.error("Axios error", {
31
- data: error.response.data,
30
+ logger.error("Axios server-side error", {
31
+ url: error.response.config.url,
32
32
  status: error.response.status,
33
33
  headers: error.response.headers,
34
+ data: error.response.data,
34
35
  });
35
36
  } else if (error.request) {
36
37
  // The request was made but no response was received
37
38
  // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
38
39
  // http.ClientRequest in node.js
39
- logger.error("Failed to receive a response from the server", {
40
- request: error.request,
41
- });
40
+ logger.error("Axios client-side error", { error: error.request });
42
41
  } else {
43
42
  // Something happened in setting up the request that triggered an Error
44
- logger.error("Error", { error });
43
+ logger.error("Axios unknown error", { error });
45
44
  }
46
45
  };
package/src/server.ts CHANGED
@@ -92,6 +92,7 @@ const scheduleController = new ScheduleController(projectStore);
92
92
 
93
93
  const mcpApp = express();
94
94
 
95
+ initProjects();
95
96
  mcpApp.use(MCP_ENDPOINT, express.json());
96
97
  mcpApp.use(MCP_ENDPOINT, cors());
97
98
 
@@ -634,3 +635,14 @@ const mcpHttpServer = mcpApp.listen(MCP_PORT, PUBLISHER_HOST, () => {
634
635
  });
635
636
 
636
637
  export { app, mainServer as httpServer, mcpApp, mcpHttpServer };
638
+
639
+ // Warm up the packages
640
+ function initProjects() {
641
+ projectStore.listProjects().then((projects) => {
642
+ projects.forEach((project) => {
643
+ projectStore.getProject(project.name!, false).then((project) => {
644
+ project.listPackages();
645
+ });
646
+ });
647
+ });
648
+ }
@@ -91,7 +91,7 @@ export async function createConnections(basePath: string): Promise<{
91
91
  const config = {
92
92
  host: connection.mysqlConnection.host,
93
93
  port: connection.mysqlConnection.port,
94
- username: connection.mysqlConnection.user,
94
+ user: connection.mysqlConnection.user,
95
95
  password: connection.mysqlConnection.password,
96
96
  database: connection.mysqlConnection.database,
97
97
  };
@@ -31,6 +31,7 @@ type ApiSchedule = components["schemas"]["Schedule"];
31
31
  type ApiColumn = components["schemas"]["Column"];
32
32
  type ApiTableDescription = components["schemas"]["TableDescription"];
33
33
 
34
+ const ENABLE_LIST_MODEL_COMPILATION = true;
34
35
  export class Package {
35
36
  private projectName: string;
36
37
  private packageName: string;
@@ -74,17 +75,43 @@ export class Package {
74
75
  ): Promise<Package> {
75
76
  const startTime = performance.now();
76
77
  await Package.validatePackageManifestExistsOrThrowError(packagePath);
78
+ const manifestValidationTime = performance.now();
79
+ logger.info("Package manifest validation completed", {
80
+ packageName,
81
+ duration: manifestValidationTime - startTime,
82
+ unit: "ms",
83
+ });
77
84
 
78
85
  try {
79
86
  const packageConfig = await Package.readPackageConfig(packagePath);
87
+ const packageConfigTime = performance.now();
88
+ logger.info("Package config read completed", {
89
+ packageName,
90
+ duration: packageConfigTime - manifestValidationTime,
91
+ unit: "ms",
92
+ });
80
93
  packageConfig.resource = `${API_PREFIX}/projects/${projectName}/packages/${packageName}`;
81
94
 
82
95
  const databases = await Package.readDatabases(packagePath);
96
+ const databasesTime = performance.now();
97
+ logger.info("Databases read completed", {
98
+ packageName,
99
+ databaseCount: databases.length,
100
+ duration: databasesTime - packageConfigTime,
101
+ unit: "ms",
102
+ });
83
103
  const connections = new Map<string, Connection>(projectConnections);
84
104
 
85
105
  // Package connections override project connections.
86
106
  const { malloyConnections: packageConnections } =
87
107
  await createConnections(packagePath);
108
+ const connectionsTime = performance.now();
109
+ logger.info("Package connections created", {
110
+ packageName,
111
+ connectionCount: packageConnections.size,
112
+ duration: connectionsTime - databasesTime,
113
+ unit: "ms",
114
+ });
88
115
  packageConnections.forEach((connection) => {
89
116
  connections.set(connection.name, connection);
90
117
  });
@@ -100,7 +127,20 @@ export class Package {
100
127
  packagePath,
101
128
  connections,
102
129
  );
130
+ const modelsTime = performance.now();
131
+ logger.info("Models loaded", {
132
+ packageName,
133
+ modelCount: models.size,
134
+ duration: modelsTime - connectionsTime,
135
+ unit: "ms",
136
+ });
103
137
  const scheduler = Scheduler.create(models);
138
+ const schedulerTime = performance.now();
139
+ logger.info("Scheduler created", {
140
+ packageName,
141
+ duration: schedulerTime - modelsTime,
142
+ unit: "ms",
143
+ });
104
144
  const endTime = performance.now();
105
145
  const executionTime = endTime - startTime;
106
146
  this.packageLoadHistogram.record(executionTime, {
@@ -117,14 +157,16 @@ export class Package {
117
157
  scheduler,
118
158
  );
119
159
  } catch (error) {
120
- logger.error("Error loading package", { error });
160
+ logger.error(`Error loading package ${packageName}`, { error });
121
161
  const endTime = performance.now();
122
162
  const executionTime = endTime - startTime;
123
163
  this.packageLoadHistogram.record(executionTime, {
124
164
  malloy_package_name: packageName,
125
165
  status: "error",
126
166
  });
127
- throw new Error("Error loading package: " + error);
167
+ throw new Error(`Error loading package ${packageName}`, {
168
+ cause: error,
169
+ });
128
170
  }
129
171
  }
130
172
 
@@ -164,13 +206,15 @@ export class Package {
164
206
  })
165
207
  .map(async (modelPath) => {
166
208
  let error: string | undefined;
167
- try {
168
- await this.models.get(modelPath)?.getModel();
169
- } catch (modelError) {
170
- error =
171
- modelError instanceof Error
172
- ? modelError.message
173
- : undefined;
209
+ if (ENABLE_LIST_MODEL_COMPILATION) {
210
+ try {
211
+ await this.models.get(modelPath)?.getModel();
212
+ } catch (modelError) {
213
+ error =
214
+ modelError instanceof Error
215
+ ? modelError.message
216
+ : undefined;
217
+ }
174
218
  }
175
219
  return {
176
220
  projectName: this.projectName,
@@ -190,7 +234,10 @@ export class Package {
190
234
  return modelPath.endsWith(NOTEBOOK_FILE_SUFFIX);
191
235
  })
192
236
  .map(async (modelPath) => {
193
- const error = this.models.get(modelPath)?.getNotebookError();
237
+ let error: Error | undefined;
238
+ if (ENABLE_LIST_MODEL_COMPILATION) {
239
+ error = this.models.get(modelPath)?.getNotebookError();
240
+ }
194
241
  return {
195
242
  projectName: this.projectName,
196
243
  packageName: this.packageName,