5
5
6
6
import { CustomUpgrades , Subscription , User } from "./types" ;
7
7
8
- // discount is the number we multiply the price by:
9
-
10
- // TODO: move the actual **data** that defines this cost map to the database
11
- // and admin site settings. It must be something that we can change at any time,
12
- // and that somebody else selling cocalc would set differently.
13
-
14
- // See https://cloud.google.com/compute/vm-instance-pricing#e2_custommachinetypepricing
15
- // for the monthly GCE prices
16
- export const GCE_COSTS = {
17
- ram : 0.67 , // for pre-emptibles
18
- cpu : 5 , // for pre-emptibles
19
- disk : 0.04 , // per GB/month
20
- non_pre_factor : 3.5 , // Roughly Google's factor for non-preemptible's
21
- } as const ;
8
+ import costVersions from "./cost-versions" ;
9
+
10
+ // version for licenses before we were using versioning; this is what
11
+ // is used when the version is not defined.
12
+ const FALLBACK_VERSION = "1" ;
22
13
23
- // Our price = GCE price times this. We charge LESS than Google VM's, due to our gamble
24
- // on having multiple users on a node at once.
25
- // 2022-06: price increase "version 2", from 0.75 → 0.8 to compensate for 15% higher GCE prices
26
- // and there is also a minimum of 3gb storage (the free base quota) now.
27
- const COST_MULTIPLIER = 0.8 ;
28
- // We gamble that projects are packed at least twice as densely on non-member
29
- // nodes (it's often worse).
30
- const NONMEMBER_DENSITY = 2 ;
31
- // Changing this doesn't change the actual academic prices --
32
- // it just changes the *business* prices.
33
- const ACADEMIC_DISCOUNT = 0.6 ;
34
- // Disk factor is based on how many copies of user data we have, plus guesses about
35
- // bandwidth to transfer data around (to/from cloud storage, backblaze, etc.).
36
- // 10 since we have about that many copies of user data, plus snapshots, and
37
- // we store their data long after they stop paying...
38
- const DISK_FACTOR = 10 ;
39
-
40
- // These are based on what we observe in practice, what works well,
41
- // and what is configured in our backend autoscalers. This only
42
- // impacts the cost of dedicated cpu and RAM.
43
- const RAM_OVERCOMMIT = 5 ;
44
- const CPU_OVERCOMMIT = 10 ;
45
-
46
- // Extra charge if project will always be on. Really we are gambling that
47
- // projects that are not always on, are off much of the time (at least 50%).
48
- // We use this factor since a 50-simultaneous active projects license could
49
- // easily be used about half of the time during a week in a large class.
50
- const ALWAYS_RUNNING_FACTOR = 2 ;
14
+ const CURRENT_VERSION = "1" ;
51
15
52
16
// Another gamble implicit in this is that pre's are available. When they
53
17
// aren't, cocalc.com switches to uses MUCH more expensive non-preemptibles.
@@ -62,23 +26,8 @@ export interface CostMap {
62
26
member : number ;
63
27
}
64
28
65
- export const CUSTOM_COST : CostMap = {
66
- ram :
67
- ( COST_MULTIPLIER * GCE_COSTS . ram ) / ACADEMIC_DISCOUNT / NONMEMBER_DENSITY ,
68
- dedicated_ram :
69
- ( RAM_OVERCOMMIT * ( COST_MULTIPLIER * GCE_COSTS . ram ) ) /
70
- ACADEMIC_DISCOUNT /
71
- NONMEMBER_DENSITY ,
72
- cpu :
73
- ( COST_MULTIPLIER * GCE_COSTS . cpu ) / ACADEMIC_DISCOUNT / NONMEMBER_DENSITY ,
74
- dedicated_cpu :
75
- ( CPU_OVERCOMMIT * ( COST_MULTIPLIER * GCE_COSTS . cpu ) ) /
76
- ACADEMIC_DISCOUNT /
77
- NONMEMBER_DENSITY ,
78
- disk : ( DISK_FACTOR * COST_MULTIPLIER * GCE_COSTS . disk ) / ACADEMIC_DISCOUNT ,
79
- always_running : ALWAYS_RUNNING_FACTOR ,
80
- member : NONMEMBER_DENSITY ,
81
- } as const ;
29
+ // BASIC, STANDARD and MAX have nothing to do with defining pricing. They
30
+ // are just some presets that might have been used at some point (?).
82
31
83
32
export const BASIC : CostMap = {
84
33
ram : 1 ,
@@ -112,36 +61,88 @@ export const MAX: CostMap = {
112
61
113
62
export const MIN_QUOTE = 100 ;
114
63
64
+ interface GoogleComputeEngine {
65
+ ram : number ;
66
+ cpu : number ;
67
+ disk : number ;
68
+ non_pre_factor : number ;
69
+ }
70
+
115
71
interface CostsStructure {
72
+ version : string ;
73
+
74
+ // these are critical to defining and computing the cost of a license
116
75
user_discount : { [ user in User ] : number } ;
117
76
sub_discount : { [ sub in Subscription ] : number } ;
118
- min_quote : number ;
119
77
custom_cost : { [ key in CustomUpgrades ] : number } ;
120
78
custom_max : { [ key in CustomUpgrades ] : number } ;
79
+ gce : GoogleComputeEngine ;
80
+
81
+ // not even sure if any of this is ever used anymore -- it's generic.
82
+ min_quote : number ;
121
83
basic : { [ key in CustomUpgrades ] : number } ;
122
84
standard : { [ key in CustomUpgrades ] : number } ;
123
85
max : { [ key in CustomUpgrades ] : number } ;
124
86
}
125
87
126
- export const COSTS : CostsStructure = {
127
- user_discount : { academic : ACADEMIC_DISCOUNT , business : 1 } ,
128
- sub_discount : { no : 1 , monthly : 0.9 , yearly : 0.85 } ,
129
- min_quote : MIN_QUOTE ,
130
- custom_cost : CUSTOM_COST ,
131
- custom_max : MAX ,
132
- basic : BASIC ,
133
- standard : STANDARD ,
134
- max : MAX ,
135
- } as const ;
88
+ export function getCosts ( version = FALLBACK_VERSION ) : CostsStructure {
89
+ const {
90
+ SUB_DISCOUNT ,
91
+ GCE_COSTS ,
92
+ COST_MULTIPLIER ,
93
+ NONMEMBER_DENSITY ,
94
+ ACADEMIC_DISCOUNT ,
95
+ DISK_FACTOR ,
96
+ RAM_OVERCOMMIT ,
97
+ CPU_OVERCOMMIT ,
98
+ ALWAYS_RUNNING_FACTOR ,
99
+ } = costVersions [ version ] ;
100
+
101
+ const CUSTOM_COST : CostMap = {
102
+ ram :
103
+ ( COST_MULTIPLIER * GCE_COSTS . ram ) / ACADEMIC_DISCOUNT / NONMEMBER_DENSITY ,
104
+ dedicated_ram :
105
+ ( RAM_OVERCOMMIT * ( COST_MULTIPLIER * GCE_COSTS . ram ) ) /
106
+ ACADEMIC_DISCOUNT /
107
+ NONMEMBER_DENSITY ,
108
+ cpu :
109
+ ( COST_MULTIPLIER * GCE_COSTS . cpu ) / ACADEMIC_DISCOUNT / NONMEMBER_DENSITY ,
110
+ dedicated_cpu :
111
+ ( CPU_OVERCOMMIT * ( COST_MULTIPLIER * GCE_COSTS . cpu ) ) /
112
+ ACADEMIC_DISCOUNT /
113
+ NONMEMBER_DENSITY ,
114
+ disk : ( DISK_FACTOR * COST_MULTIPLIER * GCE_COSTS . disk ) / ACADEMIC_DISCOUNT ,
115
+ always_running : ALWAYS_RUNNING_FACTOR ,
116
+ member : NONMEMBER_DENSITY ,
117
+ } as const ;
118
+
119
+ return {
120
+ version,
121
+
122
+ user_discount : { academic : ACADEMIC_DISCOUNT , business : 1 } ,
123
+ sub_discount : SUB_DISCOUNT ,
124
+ custom_cost : CUSTOM_COST ,
125
+ custom_max : MAX ,
126
+ gce : GCE_COSTS ,
127
+
128
+ min_quote : MIN_QUOTE ,
129
+ basic : BASIC ,
130
+ standard : STANDARD ,
131
+ max : MAX ,
132
+ } as const ;
133
+ }
134
+
135
+ const COSTS = getCosts ( CURRENT_VERSION ) ;
136
+ export { COSTS } ;
136
137
137
138
export const discount_pct = Math . round (
138
- ( 1 - COSTS . user_discount [ "academic" ] ) * 100
139
+ ( 1 - COSTS . user_discount [ "academic" ] ) * 100 ,
139
140
) ;
140
141
141
142
export const discount_monthly_pct = Math . round (
142
- ( 1 - COSTS . sub_discount [ "monthly" ] ) * 100
143
+ ( 1 - COSTS . sub_discount [ "monthly" ] ) * 100 ,
143
144
) ;
144
145
145
146
export const discount_yearly_pct = Math . round (
146
- ( 1 - COSTS . sub_discount [ "yearly" ] ) * 100
147
+ ( 1 - COSTS . sub_discount [ "yearly" ] ) * 100 ,
147
148
) ;
0 commit comments