Skip to content
Merged

Beta #1185

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
61df113
fix: catch error in draft proposal pipeline and retry with exponentia…
SewerynKras Aug 12, 2025
f5df8f3
test: test retry logic in market module
SewerynKras Aug 12, 2025
817ada7
Merge pull request #1169 from golemfactory/fix/handle-proposals-error
SewerynKras Aug 12, 2025
933df07
docs: add comment about selecting payment network in quickstart
cryptobench Jun 13, 2025
4b170a4
feat: implement cleanup for stale proposals left in cache to prevent …
SewerynKras Aug 20, 2025
be6f579
Merge pull request #1172 from golemfactory/fix/expire-old-proposals
SewerynKras Aug 20, 2025
7665e16
fix: improve handling of user-provided abort signal/timeout in `Resou…
SewerynKras Aug 21, 2025
ed6c117
fix: log only demand id, not the entire object when it gets refreshed
SewerynKras Aug 21, 2025
2614c01
test: update error message in abort activity e2e test
SewerynKras Aug 25, 2025
06043cc
Merge pull request #1174 from golemfactory/fix-abort-activity-in-test
SewerynKras Aug 25, 2025
db4968f
chore: fix some minor issues in comments (#1159)
jishudashen Sep 10, 2025
44b1b13
feat(golem-network): added getIdentity method to GolemNetwork object …
mgordel Sep 10, 2025
249f6a5
Removed changes
scx1332 Sep 29, 2025
e64e1d0
Merge pull request #1182 from golemfactory/scx1332/rem
SewerynKras Sep 29, 2025
0e274a1
fix(exe-unit): fixed setup timeout shutting down the internal executo…
SewerynKras Sep 29, 2025
61d5fb6
fix(exe-unit): fixed setup timeout shutting down the internal executo…
SewerynKras Sep 29, 2025
70d738e
refactor(exe-unit): simplify creating signal from user-defined AC/tim…
SewerynKras Sep 29, 2025
ee5579a
Merge branch 'master' into beta
scx1332 Sep 29, 2025
98caead
Merge pull request #1187 from golemfactory/fix/prevent-interrupting-e…
SewerynKras Sep 29, 2025
874a0d9
Merge pull request #1186 from golemfactory/scx1332/mergewithmain
SewerynKras Sep 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/activity/activity.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ export class ActivityModuleImpl implements ActivityModule {
this.logger.debug("Initializing the exe-unit for activity", { activityId: activity.id });

try {
await exe.setup();
await exe.setup(options?.setupSignalOrTimeout);
const refreshedActivity = await this.refreshActivity(activity).catch(() => {
this.logger.warn("Failed to refresh activity after work context initialization", { activityId: activity.id });
return activity;
Expand Down
39 changes: 35 additions & 4 deletions src/activity/exe-unit/exe-unit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,17 @@ export interface ExeUnitOptions {
/** this function is called before the exe unit is destroyed */
teardown?: LifecycleFunction;
executionOptions?: ExecutionOptions;
/**
* Abort signal or timeout for the entire lifecycle of the exe unit.
* If this signal is aborted or timeout is reached at any point, all ongoing operations
* will be stopped, no matter if it's activity deployment, setup, command execution or teardown.
*/
signalOrTimeout?: number | AbortSignal;
/**
* Abort signal or timeout for the setup phase only.
* If this signal is aborted or timeout is reached after the setup phase is completed, it will be ignored.
*/
setupSignalOrTimeout?: number | AbortSignal;
volumes?: Record<string, VolumeSpec>;
}

Expand Down Expand Up @@ -115,21 +125,41 @@ export class ExeUnit {
* This function initializes the exe unit by deploying the image to the remote machine
* and preparing and running the environment.
* This process also includes running setup function if the user has defined it
*
* @param signalOrTimeout - Abort signal or timeout for the setup phase only.
* If this signal is aborted or timeout is reached after the setup phase is completed, it will be ignored.
*/
async setup(): Promise<Result[] | void> {
async setup(signalOrTimeout?: number | AbortSignal): Promise<Result[] | void> {
const setupSignal = createAbortSignalFromTimeout(signalOrTimeout);
const throwIfAborted = () => {
if (this.abortSignal.aborted) {
throw new GolemAbortError("ExeUnit has been aborted", this.abortSignal.reason);
}
if (setupSignal.aborted) {
throw setupSignal.reason.name === "TimeoutError"
? new GolemTimeoutError("ExeUnit setup has been aborted due to a timeout", setupSignal.reason)
: new GolemAbortError("ExeUnit setup has been aborted", setupSignal.reason);
}
};
try {
throwIfAborted();
let state = await this.fetchState();
throwIfAborted();

if (state === ActivityStateEnum.Ready) {
await this.setupActivity();
return;
}

if (state === ActivityStateEnum.Initialized) {
await this.deployActivity();
await this.deployActivity(setupSignal);
throwIfAborted();
}

await sleep(1000, true);
throwIfAborted();
state = await this.fetchState();
throwIfAborted();

if (state !== ActivityStateEnum.Ready) {
throw new GolemWorkError(
Expand All @@ -141,6 +171,7 @@ export class ExeUnit {
);
}
await this.setupActivity();
throwIfAborted();
} catch (error) {
if (this.abortSignal.aborted) {
throw this.abortSignal.reason.name === "TimeoutError"
Expand All @@ -164,7 +195,7 @@ export class ExeUnit {
}
}

private async deployActivity() {
private async deployActivity(setupAbortSignal?: AbortSignal) {
try {
const executionMetadata = await this.executor.execute(
new Script([
Expand All @@ -175,7 +206,7 @@ export class ExeUnit {
new Start(),
]).getExeScriptRequest(),
);
const result$ = this.executor.getResultsObservable(executionMetadata);
const result$ = this.executor.getResultsObservable(executionMetadata, false, setupAbortSignal);
// if any result is an error, throw an error
await lastValueFrom(
result$.pipe(
Expand Down
2 changes: 1 addition & 1 deletion src/golem-network/golem-network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ export class GolemNetwork {
* @param options.order - represents the order specifications which will result in access to LeaseProcess.
* @param options.poolSize {Object | number} - can be defined as a number or an object with min and max fields, if defined as a number it will be treated as a min parameter.
* @param options.poolSize.min - the minimum pool size to achieve ready state (default = 0)
* @param options.poolSize.max - the maximum pool size, if reached, the next pool element will only be available if the borrowed resource is released or destroyed (dafault = 100)
* @param options.poolSize.max - the maximum pool size, if reached, the next pool element will only be available if the borrowed resource is released or destroyed (default = 100)
* @param options.setup - an optional function that is called as soon as the exe unit is ready
* @param options.teardown - an optional function that is called before the exe unit is destroyed
*/
Expand Down
2 changes: 1 addition & 1 deletion src/market/demand/demand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export type OrderDemandOptions = Partial<{
/** Demand properties that determine payment related terms & conditions of the agreement */
payment: Partial<PaymentDemandDirectorConfigOptions>;
}> &
/** Demand properties that determine most common paramters of the agreement (based on golemsp implementation */
/** Demand properties that determine most common parameters of the agreement (based on golemsp implementation */
Partial<BasicDemandDirectorConfigOptions>;

export interface IDemandRepository {
Expand Down
2 changes: 1 addition & 1 deletion src/market/market.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ export interface MarketModule {
}): Observable<OfferProposal>;

/**
* Estimate the budget for the given order and maximum numbers of agreemnets.
* Estimate the budget for the given order and maximum numbers of agreements.
* Keep in mind that this is just an estimate and the actual cost may vary.
* The method returns the estimated budget in GLM.
* @param params
Expand Down
2 changes: 1 addition & 1 deletion src/network/network.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ describe("Network", () => {
new GolemNetworkError(`Unable to add node network-node-id to removed network`, NetworkErrorCode.NetworkRemoved),
);
});
test("should get first avialble ip adrress", () => {
test("should get first avialble ip address", () => {
const network = new Network("network-id", "192.168.0.0/24");
expect(network.getFirstAvailableIpAddress().toString()).toEqual("192.168.0.1");
});
Expand Down
18 changes: 6 additions & 12 deletions src/resource-rental/resource-rental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,18 +143,11 @@ export class ResourceRental {
if (this.currentExeUnit !== null) {
return this.currentExeUnit;
}
const abortController = new AbortController();
this.finalizeAbortController.signal.addEventListener("abort", () =>
abortController.abort(this.finalizeAbortController.signal.reason),
);
if (signalOrTimeout) {
const abortSignal = createAbortSignalFromTimeout(signalOrTimeout);
abortSignal.addEventListener("abort", () => abortController.abort(abortSignal.reason));
if (signalOrTimeout instanceof AbortSignal && signalOrTimeout.aborted) {
abortController.abort(signalOrTimeout.reason);
}
const signal = createAbortSignalFromTimeout(signalOrTimeout);
if (signal.aborted) {
throw new GolemAbortError("Initializing of the exe-unit has been aborted", signal.reason);
}
return this.createExeUnit(abortController.signal);
return this.createExeUnit(signal);
}

/**
Expand Down Expand Up @@ -194,7 +187,8 @@ export class ResourceRental {
storageProvider: this.storageProvider,
networkNode: this.resourceRentalOptions?.networkNode,
executionOptions: this.resourceRentalOptions?.activity,
signalOrTimeout: abortSignal,
signalOrTimeout: this.finalizeAbortController.signal,
setupSignalOrTimeout: this.setupAbortController.signal,
...this.resourceRentalOptions?.exeUnit,
});
this.events.emit("exeUnitCreated", activity);
Expand Down
2 changes: 1 addition & 1 deletion src/shared/yagna/adapters/market-api-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { cancelYagnaApiCall } from "../../utils/cancel";
/**
* A bit more user-friendly type definition of DemandOfferBaseDTO from ya-ts-client
*
* That's probably one of the most confusing elements around Golem Protocol and the API specificiation:
* That's probably one of the most confusing elements around Golem Protocol and the API specification:
*
* - Providers create Offers
* - Requestors create Demands
Expand Down
13 changes: 13 additions & 0 deletions tests/e2e/resourceRentalPool.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,19 @@ describe("ResourceRentalPool", () => {
);
});

it("should not interrupt running commands on the exe-unit when aborting the setup signal after setup is done", async () => {
const pool = glm.rental.createResourceRentalPool(proposalPool, allocation, { poolSize: 1 });
const rental = await pool.acquire();
const abortController = new AbortController();
const exeUnit = await rental.getExeUnit(abortController.signal);
const runPromise = exeUnit.run("sleep 5 && echo Hello World");
// wait 2 seconds and abort the signal - the setup is already done so it should not affect the running command
await new Promise((res) => setTimeout(res, 2000));
abortController.abort();
const result = await runPromise;
expect(result.stdout?.toString().trim()).toEqual("Hello World");
});

it("should abort getting the newly created exe-unit by signal", async () => {
const pool = glm.rental.createResourceRentalPool(proposalPool, allocation, { poolSize: 1 });
const abortController = new AbortController();
Expand Down
Loading