Skip to content

Commit e44a929

Browse files
gruttabelanger5mrkaye97Copilot
authored
Fe overhaul burndown 1 (#1563)
* chore: improve error on dispatcher (#1538) * fix: empty billing context (#1553) * fix: empty * precommit * hotfix: priority nil pointer (#1555) * hotfix: priority on schedule workflow (#1556) * hotfix: priority on schedule workflow * fix: build * Hotfix: Handle EOF Properly (#1557) * fix: handle EOF properly * chore: version * fix: debug logs * fix: rm eof type * hotfix: priority on cron workflow for v0 (#1558) * fix: one more possible null deref (#1560) * Hatchet Python Blog Post (#1526) * feat: initial pass at first parts of blog post * feat: initial mkdocs setup * feat: first pass at embedding mkdocs * fix: config * debug: paths * fix: unwind docs hack * feat: start working on mkdocs theme * fix: paths * feat: wrap up post * fix: proof * fix: doc links * fix: rm docs * fix: lint * fix: lint * fix: typos + tweak * fix: tweaks * fix: typo * fix: cleanup * fix: go signature and docs (#1561) * fix: go signature and docs * Update examples/v1/workflows/concurrency-rr.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * feat: toggle doc sheet * docs: concurrency cleanup (#1562) * feat: storage adapter * docs--worker-config-options (#1535) * docs--worker-config-options * Update frontend/docs/pages/home/workers.mdx Co-authored-by: abelanger5 <belanger@sas.upenn.edu> * Update worker-configuration-options.mdx * lint --------- Co-authored-by: abelanger5 <belanger@sas.upenn.edu> --------- Co-authored-by: abelanger5 <belanger@sas.upenn.edu> Co-authored-by: Matt Kaye <mrkaye97@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 7a27ece commit e44a929

File tree

26 files changed

+649
-82
lines changed

26 files changed

+649
-82
lines changed

examples/v1/worker/start.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package main
22

33
import (
4-
"context"
54
"fmt"
65
"os"
76
"time"
87

98
v1_workflows "github.com/hatchet-dev/hatchet/examples/v1/workflows"
9+
"github.com/hatchet-dev/hatchet/pkg/cmdutils"
1010
v1 "github.com/hatchet-dev/hatchet/pkg/v1"
1111
"github.com/hatchet-dev/hatchet/pkg/v1/worker"
1212
"github.com/hatchet-dev/hatchet/pkg/v1/workflow"
@@ -82,9 +82,9 @@ func main() {
8282
panic(err)
8383
}
8484

85-
ctx, cancel := context.WithCancel(context.Background())
85+
interruptCtx, cancel := cmdutils.NewInterruptContext()
8686

87-
err = worker.StartBlocking(ctx)
87+
err = worker.StartBlocking(interruptCtx)
8888

8989
if err != nil {
9090
panic(err)

examples/v1/workflows/concurrency-rr.go

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,62 @@ import (
1313
)
1414

1515
type ConcurrencyInput struct {
16-
Message string
17-
GroupKey string
16+
Message string
17+
Tier string
18+
Account string
1819
}
1920

2021
type TransformedOutput struct {
2122
TransformedMessage string
2223
}
2324

2425
func ConcurrencyRoundRobin(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[ConcurrencyInput, TransformedOutput] {
26+
// ❓ Concurrency Strategy With Key
27+
var maxRuns int32 = 1
2528
strategy := types.GroupRoundRobin
2629

27-
// ❓ Concurrency Strategy With Key
2830
concurrency := factory.NewTask(
2931
create.StandaloneTask{
3032
Name: "simple-concurrency",
3133
Concurrency: []*types.Concurrency{
3234
{
3335
Expression: "input.GroupKey",
34-
MaxRuns: &[]int32{1}[0],
36+
MaxRuns: &maxRuns,
37+
LimitStrategy: &strategy,
38+
},
39+
},
40+
}, func(ctx worker.HatchetContext, input ConcurrencyInput) (*TransformedOutput, error) {
41+
// Random sleep between 200ms and 1000ms
42+
time.Sleep(time.Duration(200+rand.Intn(800)) * time.Millisecond)
43+
44+
return &TransformedOutput{
45+
TransformedMessage: input.Message,
46+
}, nil
47+
},
48+
hatchet,
49+
)
50+
// !!
51+
52+
return concurrency
53+
}
54+
55+
func MultipleConcurrencyKeys(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[ConcurrencyInput, TransformedOutput] {
56+
// ❓ Multiple Concurrency Keys
57+
strategy := types.GroupRoundRobin
58+
var maxRuns int32 = 20
59+
60+
concurrency := factory.NewTask(
61+
create.StandaloneTask{
62+
Name: "simple-concurrency",
63+
Concurrency: []*types.Concurrency{
64+
{
65+
Expression: "input.Tier",
66+
MaxRuns: &maxRuns,
67+
LimitStrategy: &strategy,
68+
},
69+
{
70+
Expression: "input.Account",
71+
MaxRuns: &maxRuns,
3572
LimitStrategy: &strategy,
3673
},
3774
},

examples/v1/workflows/simple.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func Simple(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[SimpleInput,
6363
if err != nil {
6464
return err
6565
}
66-
err = w.StartBlocking()
66+
err = w.StartBlocking(context.Background())
6767
if err != nil {
6868
return err
6969
}

frontend/app/src/next/hooks/use-docs-sheet.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export interface DocsSheet {
1717
export interface DocsContextValue {
1818
sheet: DocsSheet;
1919
open: (doc: DocRef) => void;
20+
toggle: (doc: DocRef) => void;
2021
close: () => void;
2122
}
2223

@@ -33,6 +34,7 @@ export function useDocs() {
3334
}
3435
return {
3536
open: context.open,
37+
toggle: context.toggle,
3638
close: context.close,
3739
sheet: context.sheet,
3840
};
@@ -61,9 +63,18 @@ export function useDocsState(): DocsContextValue {
6163
}));
6264
};
6365

66+
const toggleSheet = (doc: DocRef) => {
67+
if (sheet.isOpen) {
68+
closeSheet();
69+
} else {
70+
openSheet(doc);
71+
}
72+
};
73+
6474
return {
6575
sheet,
6676
open: openSheet,
77+
toggle: toggleSheet,
6778
close: closeSheet,
6879
};
6980
}

frontend/app/src/next/hooks/use-filters.tsx

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as React from 'react';
2+
import { useStateAdapter } from '../lib/utils/storage-adapter';
23

34
export interface FilterManager<T> {
45
filters: T;
@@ -20,6 +21,10 @@ const FilterContext = React.createContext<FilterContextType<any> | undefined>(
2021
undefined,
2122
);
2223

24+
/**
25+
* Hook to access the current filter state and filter management functions
26+
* @returns FilterManager instance with filter management functions
27+
*/
2328
export function useFilters<T>() {
2429
const context = React.useContext<FilterContextType<T> | undefined>(
2530
FilterContext,
@@ -31,35 +36,63 @@ export function useFilters<T>() {
3136
return context as FilterManager<T>;
3237
}
3338

39+
// Storage type for filters - either in-memory state or URL query parameters
40+
type FilterType = 'state' | 'query';
41+
3442
interface FilterProviderProps<T extends Record<string, any>> {
3543
children: React.ReactNode;
3644
initialFilters?: T;
45+
/**
46+
* Storage type for filters:
47+
* - 'query': Store filters in URL query parameters (default)
48+
* - 'state': Store filters in component state
49+
*/
50+
type?: FilterType;
3751
}
3852

53+
/**
54+
* Provider component that manages filter state
55+
* @param props.children - React children
56+
* @param props.initialFilters - Initial filter values
57+
* @param props.type - Storage type ('query' or 'state')
58+
*/
3959
export function FilterProvider<T extends Record<string, any>>({
4060
children,
4161
initialFilters = {} as T,
62+
type = 'query',
4263
}: FilterProviderProps<T>) {
43-
const [filters, setFilters] = React.useState<T>(initialFilters);
64+
// Initialize the storage with default values and a prefix to avoid collisions
65+
const state = useStateAdapter<T>(initialFilters, {
66+
type,
67+
prefix: 'filter_',
68+
});
4469

45-
const setFilter = React.useCallback((key: keyof T, value: T[keyof T]) => {
46-
setFilters((prev) => ({
47-
...prev,
48-
[key]: value,
49-
}));
50-
}, []);
70+
// Get current filters from the storage adapter
71+
const filters = state.getValues();
5172

52-
const clearFilter = React.useCallback((key: keyof T) => {
53-
setFilters((prev) => {
54-
const newFilters = { ...prev };
55-
delete newFilters[key];
56-
return newFilters;
57-
});
58-
}, []);
73+
// Set a single filter value
74+
const setFilter = React.useCallback(
75+
(key: keyof T, value: T[keyof T]) => {
76+
state.setValue(key as string, value);
77+
},
78+
[state],
79+
);
5980

81+
// Clear a single filter
82+
const clearFilter = React.useCallback(
83+
(key: keyof T) => {
84+
state.setValue(key as string, undefined as any);
85+
},
86+
[state],
87+
);
88+
89+
// Clear all filters
6090
const clearAllFilters = React.useCallback(() => {
61-
setFilters({} as T);
62-
}, []);
91+
const currentFilters = state.getValues();
92+
Object.keys(currentFilters).forEach((key) => {
93+
state.setValue(key, undefined as any);
94+
});
95+
}, [state]);
6396

6497
const value = React.useMemo(
6598
() => ({

frontend/app/src/next/hooks/use-pagination.tsx

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as React from 'react';
2+
import { useStateAdapter } from '../lib/utils/storage-adapter';
23

34
export interface PaginationManager {
45
currentPage: number;
@@ -26,6 +27,10 @@ const PaginationContext = React.createContext<
2627
PaginationContextType | undefined
2728
>(undefined);
2829

30+
/**
31+
* Hook to access the current pagination state and pagination management functions
32+
* @returns PaginationManager instance with pagination state and functions
33+
*/
2934
export function usePagination() {
3035
const context = React.useContext(PaginationContext);
3136
if (!context) {
@@ -34,32 +39,80 @@ export function usePagination() {
3439
return context;
3540
}
3641

42+
// Storage type for pagination - either in-memory state or URL query parameters
43+
type PaginationType = 'state' | 'query';
44+
3745
interface PaginationProviderProps {
3846
children: React.ReactNode;
3947
initialPage?: number;
4048
initialPageSize?: number;
4149
pageSizeOptions?: number[];
50+
/**
51+
* Storage type for pagination:
52+
* - 'query': Store pagination in URL query parameters (default)
53+
* - 'state': Store pagination in component state
54+
*/
55+
type?: PaginationType;
4256
}
4357

58+
// Define the type for pagination state
59+
interface PaginationState {
60+
page: number;
61+
pageSize: number;
62+
}
63+
64+
/**
65+
* Provider component that manages pagination state
66+
* @param props.children - React children
67+
* @param props.initialPage - Initial page number (default: 1)
68+
* @param props.initialPageSize - Initial page size (default: 50)
69+
* @param props.pageSizeOptions - Available page size options
70+
* @param props.type - Storage type ('query' or 'state')
71+
*/
4472
export function PaginationProvider({
4573
children,
4674
initialPage = 1,
4775
initialPageSize = 50,
4876
pageSizeOptions = [10, 50, 100, 500],
77+
type = 'query',
4978
}: PaginationProviderProps) {
50-
const [currentPage, setCurrentPage] = React.useState(initialPage);
51-
const [pageSize, setPageSize] = React.useState(initialPageSize);
79+
// Initialize the storage with default values
80+
const state = useStateAdapter<PaginationState>(
81+
{
82+
page: initialPage,
83+
pageSize: initialPageSize,
84+
},
85+
{ type },
86+
);
87+
5288
const [numPages, setNumPages] = React.useState(1);
5389

90+
// Get current values from the storage adapter
91+
const currentPage = state.getValue('page', initialPage);
92+
const pageSize = state.getValue('pageSize', initialPageSize);
93+
94+
// Set current page through the adapter
95+
const setCurrentPage = React.useCallback(
96+
(page: number) => {
97+
state.setValue('page', page);
98+
},
99+
[state],
100+
);
101+
102+
// Handle page size change through the adapter
54103
const handlePageSizeChange = React.useCallback(
55104
(newPageSize: number) => {
56-
setPageSize(newPageSize);
105+
state.setValue('pageSize', newPageSize);
106+
57107
// Calculate the new number of pages based on the new page size
58108
const newNumPages = Math.ceil((currentPage * pageSize) / newPageSize);
109+
59110
// Ensure current page is valid
60-
setCurrentPage(Math.min(currentPage, newNumPages));
111+
if (currentPage > newNumPages) {
112+
state.setValue('page', Math.min(currentPage, newNumPages));
113+
}
61114
},
62-
[currentPage, pageSize],
115+
[currentPage, pageSize, state],
63116
);
64117

65118
const value = React.useMemo(
@@ -72,7 +125,14 @@ export function PaginationProvider({
72125
setNumPages,
73126
pageSizeOptions,
74127
}),
75-
[currentPage, pageSize, numPages, pageSizeOptions, handlePageSizeChange],
128+
[
129+
currentPage,
130+
pageSize,
131+
numPages,
132+
pageSizeOptions,
133+
handlePageSizeChange,
134+
setCurrentPage,
135+
],
76136
);
77137

78138
return (
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './cn';
22
export * from './capitalize';
33
export * from './name-generator';
4+
export * from './storage-adapter';

0 commit comments

Comments
 (0)